In React Hooks, when calling setState directly during render, is the re-render guaranteed to run before the render of children?

Viewed 362

In React Hooks, we can call the setState() of a React state hook directly inside a functional component. This is described in the official React docs here.

According to the docs:

[Y]ou can update the state right during rendering. React will re-run the component with updated state immediately after exiting the first render.

What is unclear to me though is whether "first render" means the first render of the whole component tree, or just the first render of the component that called the setState()? Furthermore, what happens to effects that are scheduled during that render? Are they executed, or discarded?

This distinction is important to my use case because I'm calling setState() inside the rendering function to keep state in sync with props as described by Dan Abramov here. In my use case, the state which is synced to the prop, and the original prop, are passed down to a child. I want to be able to rely on the fact that the prop and state are in sync in the children, which will not be the case if the parent re-renders only after the whole component tree has rendered.

MY TESTS

I have tested this, and it seems like the setState() re-renders the component immediately before any child component is rendered and any effects scheduled during the initial render are discarded. This is the behavior I want. I don't know however what the specification is. Can I rely on this behavior in all cases?

THE CODE

Here is some code to illustrate this concretely:

function Foo({x}) {
    const [xPlus1, setXPlus1] = React.useState(x + 1)
    if (xPlus1 !== x + 1) setXPlus1(x + 1)
    useEffect(()=>{
        // Am I guaranteed that this will never throw?
        if (xPlus1 !== x + 1) throw new Error()
    })
    return <Bar x={x} xPlus1={xPlus1} />
}

function Bar({x, xPlus1}) {
    // Am I guaranteed that this will never throw?
    if (xPlus1 !== x + 1) throw new Error()
    return <p> {x} + 1 = {xPlus1} </p>
}
0 Answers
Related