Home > database >  How to get multiple children to set one parent state without overwriting each other react js
How to get multiple children to set one parent state without overwriting each other react js

Time:01-06

I have 3 child components that update the same parent state object. I want each child to update only their own field in the object. How do I achieve this?

I want to do this so that I can track what the state of each child is.


const Child = ({ setState, state, which }) => {
  // this was a hack to stop the infinite rerendering - ideally i don't do this but i don't know how to deal with it
  const [hasUpdated, setHasUpdated] = useState(false);
  
  useEffect(() => {
    if (!hasUpdated){
      console.log(`${which} has updated`);
      setHasUpdated(true);
      setState({
        ...state,
        [which]: false, // this can be true
        
      });
    }

  }, [setState, which, state]);
  return <div>{which === 'b' ? 'was b' : 'not b'}</div>;
};

const Parent = () => {
  const [state, setState] = useState({ a: true, b: true, c: true }); <-----------------
  // they should all be set to false but they are not - only C is set to false
// i want child a to only update the a key without affecting the rest. How do I do this?

  const children = ['a', 'b', 'c'].map((which) => {
    return (
      <div>
        <Child setState={setState} state={state} which={which} />
        ^--- {state[which] ? 'true': 'false'}
      </div>
      );
  });


  useEffect(() => {
    console.log(state);
  }, [state])

  return (<div>{children}</div>);
};

CodePudding user response:

Since they're all trying to update the state at more or less the same time, they are trampling on eachother. Each child is copying the value it has in state and then adding itself, but state stores what the component had at the time it rendered and will not take into account other updates to the state that have just taken place and are being batched by react.

You can fix this by using the function version of setState, so that you make sure you always have the latest state:

setState(prev => {
  return {
    ...prev,
    [which]: false
  }
});

CodePudding user response:

The setter of the useState hook also accepts a function as argument. This function receives the previous state as arg.

Like so:

// In Child
useEffect(() => {
  setState((prevState) => ({
    ...prevState,
    [which]: false, // this can be true
  }));
}, [setState, which]);
  •  Tags:  
  • Related