简体   繁体   中英

state update doesn't get reflected in console.log()

I have a component where I am decrementing a value contained in a state variable, every second, using a useState hook:

import React, { useEffect, useState } from "react";
import { FC } from "react";

interface ITimerProps {
  remainingSeconds: number;
}

const Timer: FC<ITimerProps> = ({ remainingSeconds = 10 }) => {
  const [remainingTime, setRemainingTime] = useState(remainingSeconds);

  useEffect(() => {
    const timer = setInterval(() => {
      decrementTime();
    }, 1000);

    return () => {
      clearInterval(timer);
    };
  }, [remainingSeconds]);

  function decrementTime  (): void  {
  
    if (remainingTime > 0) {
      setRemainingTime((currentTime) => currentTime - 1);
    }
   console.log("function gets called, state is : " + remainingTime);
  };

  return <div>{remainingTime}</div>;
};

export default Timer;

remainingTime ,which gets rendered, is indeed decremented every second, and in the developer tools, I can see that the state is updating. But the console.log() inside the decrementTime function always display a value of 10.

The if statement within decrementTime function also always checks true , as it seems that when being read within this function, the state is always 10.

Is there something that I'm missing here? Thanks

I changed the decrementTime function declaration from an arrow function to function decrementTime(){} for it to get hoisted and added it in the useEffect dependency array, now I get the correct behavior:

 import React, { useEffect, useState } from "react";
import { FC } from "react";

interface ITimerProps {
  remainingSeconds: number;
}

const Timer: FC<ITimerProps> = ({ remainingSeconds = 10 }) => {
  const [remainingTime, setRemainingTime] = useState(remainingSeconds);

  useEffect(() => {
    const timer = setInterval(() => {
      decrementTime();
    }, 1000);

    return () => {
      clearInterval(timer);
    };
  }, [remainingSeconds,decrementTime]);

  function decrementTime(): void {
    if (remainingTime > 0) {
      setRemainingTime((currentTime) => currentTime - 1);
    }
    console.log("function gets called, state is : " + remainingTime);
  }

  return <div>{remainingTime}</div>;
};

export default Timer;

Thats because the function itself that you are calling in the setInterval is not updating. You have to append that function to the dependency array of the useEffect hook. in order to avoid the eslint warning wrap the decrementTime function with the useCallback hook (doesn't make a difference because the function will change anyway on every render in that case).

import React, { useCallback, useEffect, useState } from "react";
import { FC } from "react";

interface ITimerProps {
  remainingSeconds: number;
}

const Timer: FC<ITimerProps> = ({ remainingSeconds = 10 }) => {
  const [remainingTime, setRemainingTime] = useState(remainingSeconds);

  const decrementTime = useCallback(() => {
    if (remainingTime > 0) {
      setRemainingTime((currentTime) => currentTime - 1);
    }
    console.log("function gets called, state is : " + remainingTime);
  }, [remainingTime]);

  useEffect(() => {
    const timer = setInterval(() => {
      decrementTime();
    }, 1000);

    return () => clearInterval(timer);
  }, [remainingSeconds, decrementTime]);

  return <div>{remainingTime}</div>;
};

export default Timer;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM