Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does this closure make componentDidUpdate behave like useEffect?

I was reading Dan Abramov's article: A Complete Guide to useEffect, and in the section: Each Render Has Its Own… Everything, there are two examples, first uses useEffect like this:

const UseEffectCounter = () => {
    const [count, setCount] = useState(0);
    useEffect(() => {
        setTimeout(() => {
            console.log(`You clicked ${count} times`);
        }, 3000);
    });
    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>+1</button>
        </div>
    )
}

The above example logs sequentially after the 3 seconds timeout.

The second example uses componentDidUpdate like this:

class ComponentDidUpdateCounter extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }
    componentDidUpdate() {
        setTimeout(() => {
            console.log(`You clicked ${this.state.count} times`);
        }, 3000);
    }

    render() {
        return (
            <div>
                <p>You clicked {this.state.count} times</p>
                <button onClick={() => this.setState({count: this.state.count + 1})}>+1</button>
            </div>
        )
    }
}

If click n times and wait for the timeout finishes, console logs You clicked n times for n times, rather than the sequential number of clicks. This is fine because React mutates this.state.count to always point to the latest value of the state.

However, simply modifying the componentDidUpdate like this would make the component behave like the first example:

componentDidUpdate() {
    const count = this.state.count
    setTimeout(() => {
        console.log(`You clicked ${count} times`);
    }, 3000);
}

How does this closure make the component behave like useEffect?

like image 311
thinkvantagedu Avatar asked Dec 27 '25 19:12

thinkvantagedu


1 Answers

The reason for the difference is that this.state.count is evaluated at different points in your examples.

this.state.count is evaluated in the third example after each update at the when you assign it to count, whereas in the second example this.state.count is evaluated at the end of the timeout. At the end of the timeout, all of the updates have most likely already occurred, so you see the same values logged. But assigning it to a local count variable only defers the logging, not the evaluation.

In the useEffect version, each render is creating a new count const, and a const can only be assigned once. This is why it logs each value sequentially, because a const can never be updated, even if you wait on a state update to complete.

like image 198
Brian Thompson Avatar answered Dec 30 '25 11:12

Brian Thompson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!