Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: useEffect is triggered even if the array as dependency variable wasn't changed. #30141

Open
enesgorkemgenc opened this issue Jun 29, 2024 · 4 comments
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug

Comments

@enesgorkemgenc
Copy link

React version: all

Steps To Reproduce

  1. Declare a state variable as array like: useState([1, 2, 3]);
  2. Add a useEffect including the declared state variable in the dependency array.
  3. Update the state variable with the same value.

It might be happening because if state values are checked with '==' after using functions to set states, the comparison for arrays always returns false, triggering useEffect.

[1, 2, 3] == [1, 2, 3] is false:
"This condition will always return 'false' since JavaScript compares objects by reference, not value."

Link to code example:

const [arrVar, setArrVar] = useState([]);

 useEffect(() => {
    console.log("triggered");
    console.log(arrVar);
}, [arrVar])

//Click the button twice.
return (
    <button onClick={ (e) => {setArrVar([1, 2, 3])} }>update state</button>
);

The current behavior

useEffect is triggered even if the array element in the dependency array doesn't change when updated.

The expected behavior

It shouldn't be triggered.

@enesgorkemgenc enesgorkemgenc added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Jun 29, 2024
@markerikson
Copy link
Contributor

markerikson commented Jun 29, 2024

This is expected behavior. React iterates the contents of the old and new dependency array and does a "shallow equal" comparison. In other words, if (oldDeps[i] !== newDeps[i]) runCallback().

You provided a new array reference and included that in the dependency array. Therefore, you changed the contents of the deps array, so it will run the effect callback. It doesn't matter whether the contents of that array are the same or different - what matters is the values and references directly inside of the deps array.

@mohiwalla
Copy link

This condition will always return 'false' since JavaScript compares objects by reference, not value.

You just answered yourself. If the comparison returns false, doesn't matter if array contains same data or not, it should re-render the component as the values in dependency array were found not to be equal.

@enesgorkemgenc
Copy link
Author

enesgorkemgenc commented Jun 30, 2024

@markerikson @mohiwalla Thank you for your replies. I see it is the expected behavior and It behaves the way I guessed. But is this the desired behavior? Isn't useEffect's dependency array for detecting changes and executing some code? At this point I believe what I need to do everytime just before setting a new state data as array is comparing it with it's previous value using a for loop. This operation isn't a problem for me individually. But imagine every React user having this comparison function in their code. Isn't that an unwanted state for "React"? Wouldn't it be better for cleaner code overall in React, if the useEffect's dependency array comparison function checked the arrays and other objects that are "compared by reference, not value" by javascript, by their values using a for loop or something instead of "=="? Or updating the setting state function that I mean the 2nd element returned by useState() to not update the state if the value is the same? It would only misbehave for those(?) who want to keep the same value but changing the reference, if there is so.

@ipanasenko
Copy link

ipanasenko commented Jul 2, 2024

If you want useEffect to not trigger in the example above, you need to specify dependencies array as arrVar, not [arrVar]. Otherwise you compare arrays (arrVar from this render with arrVar from previous render), which will always be false, thus triggering useEffect.
Passing arrVar as deps array will make react compare contents of arrVar instead.
Or do [...arrVar]. This will also work, but don't do this in real code, please :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug
Projects
None yet
Development

No branches or pull requests

4 participants