React update state synchronously

Viewed 67

I have a Master component that holds several Child components. Child state is kept in Master. The first state the child gets is, FORM_LOADED. Then I have the below code in my Master component.

const Master = (props) => {
  const [formStates, setFormStates] = useState()

  useEffect(() => {
    setFormStates({...formStates, Master: "FORM_LOADED"})
  },[])

  render (
    <>
      <Child view="Model1" formStates={formStates} setFormStates={setFormStates} />
      <Child view="Model2" formStates={formStates} setFormStates={setFormStates} />
      <pre>{JSON.stringify(formStates, undefined, 4)}</pre>
    </>
  )
}

const Child = (props) => {
  useEffect(() => {
    props.setFormStates({...props.formStates, [props.view]: "FORM_LOADED"})
  }, [])
  render (
    <p>{props.view}</p>
  )
}

My problem is that the state variable formStates always gets:

{
  Master: "FORM_LOADED",
  Model2: "FORM_LOADED"
}

Model1 doesn't appear in formStates. If I add more child components, only the last component is reflected in formStates. How can I get the Model1 to the formStates?

4 Answers

I'm not sure to understand everything but in a setState() method you can get the old value. You can do something like this:

  useEffect(() => {
    props.setFormStates((oldVal) =>({...oldVal, ...props.formStates, [props.view]: "FORM_LOADED" }));
  });
  return <p>{props.view}</p>;
};```

The problem might be that you're overwriting your old object with the new one every time. Try:

const Child = (props) => {
  useEffect(() => {
    props.setFormStates(prevFormStates => ({...prevFormStates, [props.view]: "FORM_LOADED"}))
  }, [])
  render (
    <p>{props.view}</p>
  )
}

Hello the approach you defined will never work because you are setting the state of the parent in the middle of a flow where a parent is in process to render its all child.

What I think is that you are trying to check if all of yours child has rendered.

React waits to mount all child components before calling componentDidMount on the parent

I think this can help you

All code from every useEffect hooks is executed before same re-rendering. That is how React works. So every one of them uses old state of formStates.

You can use following code to use current state of formState:

const Master = (props) => {
  const [formStates, setFormStates] = useState()

  useEffect(() => {
    setFormStates((prev) => ({...prev, Master: "FORM_LOADED"}))
  },[])

  render (
    <>
      <Child view="Model1" formStates={formStates} setFormStates={setFormStates} />
      <Child view="Model2" formStates={formStates} setFormStates={setFormStates} />
      <pre>{JSON.stringify(formStates, undefined, 4)}</pre>
    </>
  )
}

const Child = (props) => {
  useEffect(() => {
    props.setFormStates((prev) => ({...prev, [props.view]: "FORM_LOADED"}))
  }, [])
  render (
    <p>{props.view}</p>
  )
}
Related