Next.js Discord

Discord Forum

If statement in a useEffect block is executing both branches

Answered
Lewis's Woodpecker posted this in #help-forum
Open in Discord
Lewis's WoodpeckerOP
Hey y'all, I have a block toggle button that I'm trying to set up in a useEffect block with a click event handler for the button. When I run this code in dev, It executes both branches of the conditional, verified with logging and breakpoints. Anyone have any idea why?

  document.querySelector("#mobileToggleWrapper button")!.addEventListener("click", (e) => {
      e.preventDefault();

      if (!mobileMenuActive){
          // @ts-ignore
          document.querySelector("#mobileLinksWrapper ul")!.style.display = "flex";
          console.log("opening nav")
          mobileMenuActive = true;
          return;
      } else {
          // @ts-ignore
          document.querySelector("#mobileLinksWrapper ul")!.style.display = "none";
          console.log("closing nav")
          mobileMenuActive = false;
          return;
      }
  })
Answered by gabbagool
ill do a minimal example:

import { useState } from "react";

const links = ["/something", "/home", /*etc*/];

export const NavMenu = () => {
  const [mobileMenuActive, setMobileMenuActive] = useState(false);

  return <>
    <ul style={{ display: mobileMenuActive ? "none" : "flex" }}>
      {/* stuff here */}
    </ul>
  </>
}
View full answer

35 Replies

Lewis's WoodpeckerOP
okay, so the click event is registering twice. is there anything with useEffect that would cause this? I don't have any special event handling in my app, so what you see is all i've added.
Lewis's WoodpeckerOP
update: next is registering the event handler twice.
when I compile and run the server by itself, it only registers once
i'm so lost lol
well its either one of two thimgs
1. running in react strict mode, where code inside useEffects is run twice on the client side
2. nextjs runs react code on the server first to create an initial version of the html to load. you can do a check if its running on the client or the server with ‘typeof window !== “undefined”’
might just be
that on the server the element hasnt loaded yet?
is there a way to express what youre doing without document selectors
Lewis's WoodpeckerOP
idk if this answers your question much, but this is a client component. i never explicitly enabled strict mode, unless next does that by default
my next config is empty.
(i'm still a bit new to react-specific syntax and next if you can't tell lol /s)
Lewis's WoodpeckerOP
basically the goal of this is:

when the page is low width, the navbar will display a toggle button where the links would usually be. When the toggle is clicked, a hardcoded replacement menu will render into the dom, and if the button is clicked again, the replacment menu will be removed. relatively a simple thing, and I'm pretty much just porting over the way i'd handle it in vanilla JS. If there's a better way I can do this with hooks, state, and CSS, let me know 🙂
i.e. putting "use client" at the top of your file means they behave the same as components in the pages router
@Lewis's Woodpecker (i'm still a bit new to react-specific syntax and next if you can't tell lol /s)
ah yeah no worries man everyones been through that stage - happy to help :)
ill do a minimal example:

import { useState } from "react";

const links = ["/something", "/home", /*etc*/];

export const NavMenu = () => {
  const [mobileMenuActive, setMobileMenuActive] = useState(false);

  return <>
    <ul style={{ display: mobileMenuActive ? "none" : "flex" }}>
      {/* stuff here */}
    </ul>
  </>
}
Answer
i think thats what ur going for?
and then make ur button change the value using setMobileMenuActive
Lewis's WoodpeckerOP
okay, i switched it over to using state based changes to the CSS, as suggested above, but react is still registering two click handlers for the toggle button when in dev (doesn't post-build)
this is what the file looks like atm
(ignore the navBlurred stuff, that's something else that I'm playing around with atm
ah cool - ill have a look in a half hour or so
so you could turn it off
but the purpose of it running the code twice is to force you to not think of it as code that runs once
maybe try adding
if (typeof window !== "undefined") {
  // window stuff here to only run on the client
}
also
maybe add a cleanup function to the useeffect
i.e. return () => window.removeEventListener("scroll", onScrollFunction);
moving the logic you have in the arrow function to onScrollFunction
i would say its probably strict mode though
Lewis's WoodpeckerOP
that way of doing it works for me with some slight modifications. i'll mark it as correct