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

[React 19]useEffect cleaned the wrong state in StrictMode #31098

Open
dislido opened this issue Sep 30, 2024 · 2 comments
Open

[React 19]useEffect cleaned the wrong state in StrictMode #31098

dislido opened this issue Sep 30, 2024 · 2 comments
Labels

Comments

@dislido
Copy link

dislido commented Sep 30, 2024

Summary

https://codesandbox.io/p/sandbox/youthful-orla-lk7g89

import { useEffect, useState } from "react";

let stateId = 0;
export default function App() {
  const [state] = useState({
    stateId: console.log(++stateId, "created") || stateId,
    destroy() {
      console.log(`state ${this.stateId} destroyed`);
    },
  });
  useEffect(() => {
    return () => {
      state.destroy();
    };
  }, [state]);

  console.log("render state", state);
  return <div className="App">state: {state.stateId}</div>;
}

console output:

1 created
render state {stateId: 1, destroy: ƒ}
2 created
render state {stateId: 1, destroy: ƒ}
state 1 destroyed

Final render: state: 1, the destroyed state

[email protected] behavior

related issue: #29634

1 created
render state {stateId: 1, destroy: ƒ}
2 created
render state {stateId: 2, destroy: ƒ}
state 2 destroyed

Final render: state: 2, the destroyed state

@MohamedYassineBenomar
Copy link

This issue seems interesting! I would love to contribute or discuss further. 👍

@ry-krystal
Copy link

This behavior is likely due to how React handles state and component updates in the given version. It seems like React is treating the useState call as if it is creating a new state object each time the component re-renders, which should not happen in a normal scenario. The behavior can be linked to potential issues or changes in state handling, particularly in versions around [email protected] as mentioned in the screenshot。

Solution and Recommendation:
Ensure Stable useState Behavior: Make sure that useState is only called once for the same component instance, without causing unnecessary state recreation. This can be achieved by ensuring that the initialization logic is stable and not tied to re-renders.

Verify React Version and Known Issues:

Check the React version you are using and look for related issues in the React repository. The issue link (#29634) mentioned in the screenshot might provide more context.
Consider upgrading or downgrading React to see if this behavior changes, as it could be a bug or regression in a particular version.
Alternative State Management: If this behavior is confirmed as a bug or unintended side effect, consider using a different state management approach, such as useRef or useReducer, to maintain stable references and avoid unexpected destruction of state objects.

Modified Code Example (If Needed):
To illustrate a more stable approach using useRef instead of useState for maintaining object references:

import { useEffect, useRef } from "react";

let stateId = 0;
export default function App() {
const stateRef = useRef({
stateId: console.log(++stateId, "created") || stateId,
destroy() {
console.log(state ${this.stateId} destroyed);
},
});

useEffect(() => {
return () => {
stateRef.current.destroy();
};
}, []);

console.log("render state", stateRef.current);
return

state: {stateRef.current.stateId}
;
}
Key Changes:
Replaced useState with useRef to maintain a stable reference to the state object.
This ensures that the destroy method is only called when the component is unmounted, avoiding unexpected destruction of the state object.
By understanding the root cause and applying the appropriate fix, the component can behave predictably across different React versions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants