Skip to content
Advertisement

React hook useState with old state on children’s first render useEffect

Example: https://codesandbox.io/s/react-hooks-playground-forked-15ctx?file=/src/index.tsx

One parent with one useState hook and 3 children with a useEffect hooks. I want the children to update the state in the parent via a callback so that all the components state are updated into one object in the parent. This does not work for the initial/first render as the current state does not update between each useEffect triggered callback (see example above, or code below). This means the combined state consists of only the state from the last child.

const ChildElement = ({ index, callback }) => {
  useEffect(() => {
    callback(index);
  }, []);
  return <>{index}</>;
};

const App = () => {
  const [state, setState] = useState(0);

  const callback = (index: any) => {
    const newObject = { a: index };
    setState({ ...state, [index]: newObject });
  };

  console.log(state);

  return (
    <>
      <ChildElement index={0} callback={callback} />
      <ChildElement index={1} callback={callback} />
      <ChildElement index={2} callback={callback} />
    </>
  );
};
  • I could declare the initial state in the parent, but that’s more code
  • I could use in render cache (an object I manually update like useStae, but changed immediately), but that feels dirty
  • Is the hook useReducer a good solution here?

Any suggestions? What’s the best approach to solve this problem?

Answer

You are facing a race conditions.

if you change

const callback = (index: any) => {
    const newObject = { a: index };
    setState({ ...state, [index]: newObject });
  };

to:

  const callback = (index: any) => {
    const newObject = { a: index };
    setState((currentState) => ({ ...currentState, [index]: newObject }));
  };

your code will work

although this:

all the components state are updated into one object in the parent

is not a good practice.

its better child components to have their own state and you can reduce code by creating a custom hook if those state act like each other.

Advertisement