Home > database >  Unexpected behaviour using React useEffect hook
Unexpected behaviour using React useEffect hook

Time:11-22

I started studying reaction hooks. But I have some doubts. I thought I understood, but the empirical evidence shows unexpected behavior, at least as far as I believe I know.

To illustrate my perplexities or misunderstandings, I take the classic exercise that implements a simple clock as an example. Below is the code that implements the functional component.

import {useState, useEffect} from 'react';
 
const Clock = (props) => {
 
    const {show, country, timezone} = props;
    const t = Date.now ()   3600 * timezone * 1000;
    const dateInitial = new date (t);
    const [datE, setDate] = useState (dateInitial);
 
    useEffect (() => {
        const interval = setInterval (() => {
            const t = date.getTime ()   (1 * 1000);
            setDate (new date (t));
        }, 1000);
        return () => {
            // ONLY clears the return of this function
            // When the component is unmounted from the DOM
           console.log ("Cleanup function (componentWillUnmount ())");
           clearTimeout (interval); // The setTimeout is cleared as soon as the component is unmounted from the DOM
        }
    }, [date);
 
    return (
        <h2 style = {{display: show? true: 'none'}}> Today in {country} is {date.toLocaleDateString ()   ''   date.toLocaleTimeString ()} </h2>
    )
};
 
export default Clock;

The functional component uses useEffect hooked to the "date" state property.

The function:

() => {
            const t = date.getTime ()   (1 * 1000);
            setDate (new date (t));
        }, 1000);

Passed as the first parameter to useEffect, it should be used for componentDidMount and componentDidUpdate life cycle events.

While the cleanup function:

return () => {
            // ONLY clears the return of this function
            // When the component is unmounted from the DOM
           console.log ("Cleanup function (componentWillUnmount ())");
           clearTimeout (interval); // The setTimeout is cleared as soon as the component is unmounted from the DOM
        }

it should be executed at the componentWillUnmount event of the component lifecycle.

But if I "run" our component, I see this in the logs:

enter image description here

As you can see, the componentWillUnmount event occurred every second. If I'm not wrong, shouldn't the componentWillUnmount happen when the component is removed from the DOM? In this circumstance, we only have a new rendering of the DOM and not the removal of the element from the DOM. I am perplexed. On the other hand, if you try the following example with the class component:

Clock with class component (on codepen.io)

You will see that console.log ("componentWillUnmount"); never appears ...

I also observe that the component that I inserted in the post loses several seconds if I change the tab, while with the one on codepen.io it does not lose a "beat".

Excuse me, I'm a little confused.

I thank you in advance for your always precious clarifications.

CodePudding user response:

useEffect represent componentDidMount and componentWillUnmount only if you give it an empty dependency array:

useEffect(() => {
    //code that will only be executed when component mount
    return () => {
        //code that will only executed when component unmount
    }
}, [])

In your example, you have date in the dependency array, this mean, every-time date changes, a new execution, think about it like that useEffect is a mini component which it's life depends on the date value every-time date changes it unmout and remount.

The reason we need the return function in such case, is when we have for example a listener that depend on the date value, so each time the date chnage we need to remove the old listener with old value and attach new listener with new value.

useEffect(() => {
    //We attach a listener which depends on date value
    const listenerHandler = () => {console.log(date)}
    window.addEventListener('event', listenerHandler)

    return () => {
       //We need to remove the listener with old value so we don't have many listeners attached each time date changes
       window.removeEventListener('event', listenerHandler)
    }
}, [date])
  • Related