Search code examples
reactjsreact-hooksuse-statereact-functional-component

Why is initial state being set over and over again in the "useState" hook of react


I have created a function which is listening for the resize event it checks for the window width and updates my IsMobile variable if(width<=600 and IsMobile = false) then set it to true and vice versa.

here is the code:

import React, { useEffect, useState } from 'react';

export interface MechnicalLiftProps {
}

const MechnicalLift: React.SFC<MechnicalLiftProps> = () => {

    const [IsMobile, setIsMobile] = useState(true)

    const handleWindowChange = () => {

        console.log('window inner width', window.innerWidth);
        console.log('is mobile in handle', IsMobile);

        if (window.innerWidth <= 600 && IsMobile === false) {
            setIsMobile(true);
            console.log('is mobile set to true');
        }

        if (window.innerWidth > 600 && IsMobile === true) {
            setIsMobile(false);
            console.log('is mobile set to false');
        }

    }

    useEffect(() => {
        console.log('mount running');
        window.addEventListener('resize', handleWindowChange)
        handleWindowChange()
        return () => {
            window.removeEventListener('resize', handleWindowChange)
        }
    }, [])

    useEffect(() => {
        console.log('is this mobile ', IsMobile);
    }, [IsMobile])

    return (
        <div>

        </div>
    );
}

export default MechnicalLift;

my console output:

enter image description here

i feel initial state is being set every time the component is re rendered otherwise why is IsMobile set to true every time in handle window change even though its set multiple times to false. new to hooks please elaborate your explanation as much as possible


Solution

  • Because of the empty array on the useEffect, you are setting up the event listener exactly once, on the first render. The function you create has a reference to IsMobile from that first render, and it's value is true. Its value will never change.

    If you need to know the most recent value of the state, you can use the callback version of set state:

    const handleWindowChange = () => {
      setIsMobile(prev => {
        console.log('previous value', prev);
        if (window.innerWidth <= 600 && prev === false) {
          return true;
        }
        if (window.innerWidth > 600 && prev === true) {
          return false;
        }
        return prev;
      });
    });
    

    By the way, setting state won't rerender if the value didn't change, so you don't need to guard against setting it to a value that it already has. In other words, if you don't need the logging, then your code can be simplified to this:

    const handleWindowChange = () => {
      setIsMobile(window.innerWidth <= 600);
    }