A game screen keeps a running score and a separate sound toggle. Accumulating points and changing the sound setting are independent actions that should not interfere with each other.
Click Score +10 several times to build up a score, then click the Sound toggle. The score immediately drops back to zero.
The child's effect fires when it detects a change in one of its dependencies - consider what constitutes a change for a value that is a function.
The child's effect fires when it detects a change in one of its dependencies - consider what constitutes a change for a value that is a function.
Why this fixes it
Every time GameScreen re-renders, const handleComplete = () => {} allocates a brand-new function at a new memory address. Because ScoreBoard's useEffect lists onComplete as a dependency, it fires whenever that reference changes — which is on every parent re-render, including the one caused by the sound toggle. Each re-run calls setScore(0), wiping the accumulated score. Wrapping the callback in useCallback(() => { ... }, []) memoizes the function so the same reference is returned on every render, and the effect only runs on mount, leaving the score untouched by any unrelated parent state change.