Home > front end >  Why does using setState in React makes function repeat?
Why does using setState in React makes function repeat?

Time:01-22

There is a button that toggles dark and light mode so I am trying to save the state of what mode it is on in localStorage. However, when I try to change the state to anything inside the React function it calls the function infinitely and gives this error: Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.

How do I change the state without re-rendering the function infinitely?

let localDark = localStorage.getItem("dark");

function Mode() {
    const [dark, setDark] = useState(false);

    const onClick = () => {
        if (dark) {
            setDark(false);
            document.querySelector("body").classList.remove("dark");
        } else {
            setDark(true);
            document.querySelector("body").classList.add("dark");
        }
        localStorage.setItem("dark", dark);
    };

    if (localDark !== null) {
        localDark = JSON.parse(localStorage.getItem("dark"));
        setDark(localDark); // this is what causes the error
        // onClick();
    }

    return (
        <div onClick={onClick} className="mode">
            {dark ? <Light /> : <Dark />}
        </div>
    );
}

CodePudding user response:

The issue you're encountering is because the setDark(localDark) line inside the if (localDark !== null) block is causing the component to re-render, which then triggers the if (localDark !== null) block again, causing an infinite loop.

To fix this, you can use the useEffect hook to handle the local storage logic. The useEffect hook allows you to run side-effects, such as setting up a subscription or manually changing the DOM, after the component has rendered.

Here's an example of how you can update the code to use useEffect:

function Mode() {
    const [dark, setDark] = useState(false);

    const onClick = () => {
        if (dark) {
            setDark(false);
            document.querySelector("body").classList.remove("dark");
        } else {
            setDark(true);
            document.querySelector("body").classList.add("dark");
        }
        localStorage.setItem("dark", dark);
    };

    useEffect(() => {
        const localDark = JSON.parse(localStorage.getItem("dark"));
        if (localDark !== null) {
            setDark(localDark);
        }
    }, []);

    return (
        <div onClick={onClick} className="mode">
            {dark ? <Light /> : <Dark />}
        </div>
    );
}

This way, the logic is only run once, when the component first renders, and so it avoids the infinite loop.

Additionally, You can also pass dark as the second argument to the useEffect hook, so that the effect only runs when the dark state changes, this way the effect will only run when the dark state is changed, this will prevent the infinite loop.

useEffect(() => {
    const localDark = JSON.parse(localStorage.getItem("dark"));
    if (localDark !== null) {
        setDark(localDark);
    }
}, [dark]);

This way, the effect will only run when the dark state changes, which will prevent the infinite loop.

CodePudding user response:

Using setState in React will cause the function to repeat because it is triggering a re-render of the component.

To prevent this, you should use the useEffect hook with an empty array as a dependency to set the state when the component mounts. This way, the state will only be set once and the useEffect hook will not trigger a re-render.

  • Related