0

My component is:

export default function TopHalf(props) {
    const [startDegrees, setStartDegrees] = useState(0)

    useEffect(() => {
        const centerPoint = { x: dim.width / 2, y: dim.height / 4 }
        setCenter(centerPoint)

        setInterval(() => {
            console.log('startDegrees', startDegrees)
            setStartDegrees(startDegrees + 5)
        }, 500)
    }, [])
...
    return (
        <HalfView style={{ top: 0, position: 'absolute', width: '100%' }} >
            {props.participants?.map(circularParticipants)}
        </HalfView>
    )
}

so you can see that startDegrees should increase by 5 every half second. But it stays at 0 when I log it out.

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
Shamoon
  • 41,293
  • 91
  • 306
  • 570
  • 2
    Just so you know, I didn't downvote you. – Emile Bergeron May 06 '20 at 21:44
  • 2
    What happened is explained pretty good in [this article](https://dmitripavlutin.com/react-hooks-stale-closures/). Basically startDegrees is a stale closure, if you carefully inspect how startDegrees is available through a closure and how closures work then you can understand why what you created did not work. You can fix it by passing a callback to setStartDegrees and do `setStartDegrees(current=>current + 5) ` or better yet; have setStartDegrees be a dependency of the effect and do a setTimeout that can be cleared in the effect cleanup function. – HMR May 06 '20 at 22:44

1 Answers1

3

From the react documentation:

Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.

https://reactjs.org/docs/react-component.html#setstate

In this case, react has likely not updated the state, so the logging is reading the original value. To avoid the concurrency issues with setState, you can pass a callback as a second parameter that is called when the setState is executed. That way it will always grab the value directly before updating.

There is more information in the link above. Hope this helps!

Kvdgulik
  • 46
  • 2
  • 1
    The solution you're suggesting is right, but the explanation and the link is off. OP is using hooks, not a class component and the problem is not about the delay from setting the state, but about the closure being defined once and capturing the `startDegrees` only once on mount at its initial value. – Emile Bergeron May 06 '20 at 21:39
  • 1
    The [`useState` documentation](https://reactjs.org/docs/hooks-reference.html#usestate) would be a better suited reference here. – Emile Bergeron May 06 '20 at 21:41