Next.js Discord

Discord Forum

useState doesn't work in NextJS 13

Answered
American Sable posted this in #help-forum
Open in Discord
Avatar
American SableOP
I should have used NextJS 12...

The two major issues I’m having:
- Navigation: When using back/forward buttons in the browser, the URL changes, but the page does not.
- Toggling Elements: Whenever I try to toggle an element using the useState function, nothing happens.

Honestly, I suspect there’s far more that could be fixed. This is my first NextJS project. I’m pretty sure I could fetch my data from WordPress in a better way.

If someone would be willing to look at my repo and tell me what I'm doing wrong, I'd be truly grateful.
https://github.com/davidmatthewcoleman/site

I started my coding days in May 2015 with PHP and WordPress. Until 5 days ago it was all that I knew.
I'd tried NextJS and Gatsby in the past, but always gave up do to how daunting learning something new was.
I tried to find a good WordPress & NextJS starter template to no success, so I decided to build this from scratch.
NextJS 13 is so new though, that many of the issues I have I’m unable to find documentation on. There's nearly nothing about NextJS 13 on reddit or StackOverflow.

getServerSideProps is no longer a thing from what I understand, so I'm using fetch. I don't want to use GraphQL, as with WordPress' own REST API I can easily manipulate the data it generates.

Also, I do wonder if Vercel's free plan is enough. Will it only work as a 100% static site, or will it update when I add changes on WordPress? Will I have to deploy everytime I add a page, a blog post, or edit the menu?
Unfortunately I'm unable to test a live production site at the moment, as I'm having financial problems and thus can't afford to host WordPress, so all my testing and dev-work has to be done locally on my Mac.
Luckily, in the next two weeks I should be able to get either the two or four year plan with Hostinger.

Please, if someone could help me, I'd be extremely grateful.
I'm sorry if I'm oversharing, or being weird or anything. I'm Autistic, so I guess that's just the way I am.
Image
Answered by American Sable
So I managed to solve this, and oddly enough ChatGPT helped a bit.
I've now separated my fetches into a separate api.tsx component.
I will say, I don't like that on initial load, the logo and menu take a moment to appear, but that's a problem for another day.
View full answer

74 Replies

Avatar
American SableOP
First, I'd like to solve the issue with toggling elements.
This is my [header.tsx](https://github.com/davidmatthewcoleman/site/blob/main/src/app/components/header.tsx) file. It's the only place I'm attempting to toggle an element (so far).
Things I want to do in order:
- Solve the toggle problem
- Fix navigation
- Change how I'm querying WordPress (I still want to use the REST API, but just use a different method like SWR.)
- Clean up my code, make it consistent, etc.
Once that's all completed, I can focus on more features, styling, etc...
Please do take a look at the repo though, and feel free to give me advice/feedback on anything else I can do to improve it.
As mentioned above, up until this past week, I've been solely a WordPress developer.
I have no plans of doing web design as a career. I'm a hobbyist.
Avatar
American SableOP
If you do want to help me, and decide to clone my repo, I'd recommend installing [localwp](https://localwp.com/). My NextJS site relies on modified entries to the REST API, so you'll need to install my [core plugin](https://github.com/davidmatthewcoleman/core) for WordPress in order for my site to work properly.
Please note: my core plugin relies on Advanced Custom Fields Pro (I can send a copy, but will not give my license key which I bought before they moved to a subscription model).
Also, the plugin is a bit of a mess code-wise right now. I'm hoping to clean it up nice and tidy and add comments sometime before I launch my production site.
I'm not sure what the server rules are on sharing ACF though, so I might not be allowed to do that.
Unfortunately, that does complicate things. You might be able to get by with the free version, but I'm not sure. I haven't used free ACF (which I believe still uses version 4, which their on 6 now) since 2016.
Avatar
American SableOP
Currently you can edit the WordPress URL for NextJS in lib/info.tsx, though I hope to make that an environment variable.
My NextJS site sees what template a WP page is using (currently just about-page.php and reading-list-page.php) which are in my [theme](https://github.com/davidmatthewcoleman/theme-2). ACF shows different options depending on the page's template. You will need to add Goodreads list RSS feeds to the WP reading list page (whichever one is using the reading-list-page.php template). Though that's only if you want to test the reading list page.
Avatar
American SableOP
Also, I should mention, the menu is also managed by WordPress, so any changes to that will need to be done there.
Avatar
American SableOP
Also, make sure you get the internal menu post ID (not menu-1 in the theme) from WordPress and put it in lib/info.tsx
WordPress menus are their own post-type, and thus they have a post id. You can get it by copying it from the URL when editing the menu.
Avatar
Alfonsus Ardani
Also, I do wonder if Vercel's free plan is enough. Will it only work as a 100% static site, or will it update when I add changes on WordPress?
if you set it with ISR, such that your fetch revalidates every n second you don't have to rebuild it or redeploy it. If you add change to wordpress, just wait until the fetch are invalidated after n amount of time then vercel will automatically rerender the pages
Avatar
American SableOP
Awesome, that's good.
Avatar
Alfonsus Ardani
Also
you should probably focus on one problem on one post instead of posting multiple ones it makes people hard to follow which one you are trying to fix first
Avatar
American SableOP
Ok, sorry.
Avatar
Alfonsus Ardani
its ok
i understand that many question can appear all at once
furthermore, i am not sure if ure still using the app routing or not
Avatar
American SableOP
I am using the src/app folder if that's what you mean?
Avatar
Alfonsus Ardani
yes if you use the src/app folder it means you are using nextjs13's app router feature
as opposed to src/pages which is pages routing
Avatar
American SableOP
Would migrating help, and how difficult would that be to do?
Avatar
Alfonsus Ardani
migrating from which
Avatar
American SableOP
From app to pages
Avatar
Alfonsus Ardani
you want to switch the older one?
Avatar
American SableOP
If that would fix my problems... maybe?
Avatar
Alfonsus Ardani
migrating from pages to app is already challenging
but im not sure about the other way around 🙃
Avatar
American SableOP
Darn
Avatar
Alfonsus Ardani
So just focus to one problem and change the title to be more specific if you want people to help you
Avatar
American SableOP
Ok, thanks for the advice.
Avatar
Alfonsus Ardani
something like "use state doesn't work in nextjs 13"
what exactly doesn't work? any error codes? how did you implement them? what did you try?
Avatar
American SableOP
I've tried everything I could think of. Depending on how I do it, the state boolean may or may not toggle. No matter what I do, I can't modify the DOM.
Avatar
Alfonsus Ardani
any error messages?
Avatar
American SableOP
Nope. In fact, running npm run build works just fine.
Avatar
Alfonsus Ardani
any runtime error messages? :v
Avatar
American SableOP
Nope
Avatar
Alfonsus Ardani
how did you implement them
Avatar
American SableOP
Just a moment...
Sorry.
Here's my code.

const [toggle, setToggle] = useState(false);

  const toggleNav = () => {
    setToggle((prevToggle) => !prevToggle); // Update the toggle state when the button is clicked
  };

  useEffect(() => {
    const handleEscapeKey = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setToggle(false);
      }
    };

    document.addEventListener("keydown", handleEscapeKey);

    return () => {
      document.removeEventListener("keydown", handleEscapeKey);
    };
  }, []);
Avatar
Alfonsus Ardani
oh so its the escape toggle that sometimes works and doesn't work?
Avatar
American SableOP
The whole thing.
Avatar
Alfonsus Ardani
did you use 'use client'?
Avatar
American SableOP
Yes
Avatar
Alfonsus Ardani
have you tried it in a minimal reproduction repository
have you tried console.logging in useEffect and see if it got printed out
have you check that that component is rendered properly
https://nextjs-demos-git-dynamic-routes-alfonsusac.vercel.app/
this is using useState to change the numbers every second and it works fine
Avatar
American SableOP
Not sure what the first one is. I have logged the console. Depending on my code, it sometimes toggles between true and false.
Avatar
American SableOP
Shoot, that code was when I tried to get ChatGPT's help. Let me run time machine real quick.
Here you go.

const [toggle, setToggle] = useState(false);

    const toggleNav = () => {
        setToggle((toggle: boolean) => {
            document.body.setAttribute('data-menu', `${!toggle ? 'show' : 'hide'}`);
            console.log(toggle);
            return !toggle;
        });
    }
And yes, I was toggling an attribute on body before, but I don't want to use that.
console.log(toggle) shows the value changes. The dom however does not.
<div className="block md:hidden text-base leading-5">
                <div id="mobileMenu" className={`absolute ${toggle ? 'block' : 'hidden'} py-7 inset-0`}>
                    <Menu />
                </div>
                <button onClick={toggleNav} className="relative ml-2 bg-transparent cursor-pointer bg-opacity-20 transition-all duration-100 md:hover:-translate-y-1 rounded-full hover:bg-gray-100/75 dark:hover:bg-gray-800/75 hover:backdrop-blur-sm border border-solid border-white/0 outline outline-1 outline-neutral-200/0 hover:border-white hover:outline-neutral-200 dark:outline-neutral-950/0 dark:hover:border-white/5 dark:hover:outline-neutral-950 hover:shadow z-20" aria-label="Toggle Menu" type="button" style={{transform: 'none'}}>
                    <div className="flex h-8 w-8 items-center justify-center p-2">
                        [redacted SVG]
                    </div>
                </button>
            </div>
Avatar
American SableOP
Started a new project, and it worked.
Image
In my main project, I commented out <Menu /> and <Logo /> and it toggles the class.
So something about those components is preventing me from manipulating their parent elements.
It only works if I specifically remove both <Menu /> components and the <Logo /> component.
Just removing one or two doesn't fix it.
Could it be because those are async?
Avatar
American SableOP
You still there?
Avatar
American SableOP
Commenting out the return in logo and menu doesn't work either.
Avatar
Alfonsus Ardani
Client comps cant be async
Avatar
American SableOP
Here's menu.tsx

import Link from "next/link";
import { Site, menuID } from "../../../lib/info";

interface MenuItem {
  url: string;
  title: string;
}

async function Menu() {
  const res = await fetch(`${Site}/wp-json/options/menu/${menuID}`, { next: { revalidate: 10 } });
  const data: MenuItem[] = await res.json();

  return (
    <>
      {data.map((item) => (
        <Link key={item.url} href={item.url} passHref legacyBehavior>
          <a className="menu-item relative py-1 px-2 bg-transparent bg-opacity-20 transition-all duration-100 md:hover:-translate-y-1 rounded hover:bg-gray-100/75 dark:hover:bg-gray-800/75 hover:backdrop-blur-sm border border-solid border-white/0 outline outline-1 outline-neutral-200/0 hover:border-white hover:outline-neutral-200 dark:outline-neutral-950/0 dark:hover:border-white/5 dark:hover:outline-neutral-950 hover:shadow z-20">
            {item.title}
          </a>
        </Link>
      ))}
    </>
  );
}

export default Menu;
and logo.tsx

import WpImage from "./wpimage";
import { Site } from "../../../lib/info";

interface LogoData {
  site_logo: number;
  name: string;
}

async function Logo() {
  const res = await fetch(`${Site}/wp-json/`, { next: { revalidate: 10 } });
  const data: LogoData = await res.json();

  if (data.site_logo) {
    return (
      <>
        <WpImage
          id={data.site_logo}
          width={32}
          height={32}
          alt={data.name}
          className="rounded-full outline outline-2 outline-black/25 dark:outline-white/25 mr-2"
        />
        <span>{data.name}</span>
      </>
    );
  }
  
  return null;
}

export default Logo;
Playing around with those, I've found that fetching is the issue.
Avatar
American SableOP
So I managed to solve this, and oddly enough ChatGPT helped a bit.
I've now separated my fetches into a separate api.tsx component.
I will say, I don't like that on initial load, the logo and menu take a moment to appear, but that's a problem for another day.
Answer