Reputation: 7529
I have the following scenario. I want to register a function in the state of my parent component. That function closes over some local state in the child component. The function seems to always get a stale value of history
when invoked in the parent component.
import React, { useState, useCallback } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [parentState, setParentState] = useState([]);
const [inc, setInc] = useState(0);
return (
<span>
<StaleList setParent={setParentState} />
Read from closures: {parentState.map(fn => fn())}
<br />
Rerenders from button press: {inc}
<button onClick={() => setInc(val => val + 1)}>Trigger rerender</button>
</span>
);
};
const StaleList = ({ setParent }) => {
const [history, setHistory] = useState([]);
const closure = () => history;
return (
<div>
History: {history}{" "}
<button
onClick={() => {
setHistory(["new history value"]);
setParent([closure]);
}}
>
Click me
</button>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("container"));
How can I fix the stale closure here, and have the closure return the latest value of history
when invoked? (ie. see "new history value" reflected in the parent)
Upvotes: 1
Views: 1172
Reputation: 281764
The issue with your code is that the closure function has the old value of hisotry when you push it to parentState on button click.
You can instead make use of useEffect to update the parent
const StaleList = ({ setParent }) => {
const [history, setHistory] = useState([]);
useEffect(() => {
const closure = () => history;
setParent([closure]);
}, [history, setParent]);
return (
<div>
History: {history}{" "}
<button
onClick={() => {
setHistory(["new history value"]);
}}
>
Click me
</button>
</div>
);
};
Upvotes: 1