ᴘᴀɴᴀʏɪᴏᴛɪs
ᴘᴀɴᴀʏɪᴏᴛɪs

Reputation: 7529

Stale closure when storing callback in parent component state

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)

Playground link

Upvotes: 1

Views: 1172

Answers (1)

Shubham Khatri
Shubham Khatri

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>
  );
};

Edit FORM VALUES

Upvotes: 1

Related Questions