Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
4.7k views
in Technique[技术] by (71.8m points)

reactjs - React state not updating inside setInterval

I'm trying to learn React with some simple projects and can't seem to get my head around the following code, so would appreciate an explanation.

This snippet from a simple countdown function works fine; however, when I console.log, the setTime appears to correctly update the value of 'seconds', but when I console.log(time) immediately after it gives me the original value of 3. Why is this?

Bonus question - when the function startCountdown is called there is a delay in the correct time values appearing in my JSX, which I assume is down to the variable 'seconds' being populated and the start of the setInterval function, so I don't get a smooth and accurate start to the countdown. Is there a way around this?

const [ time, setTime ] = useState(3);
const [ clockActive, setClockActive ] = useState(false);

  
  function startCountdown() {
    let seconds = time * 60;
    setClockActive(true);
    let interval = setInterval(() => {
      setTime(seconds--);
      console.log(seconds); // Returns 179
      console.log(time); // Returns 3
        if(seconds < 0 ) {
        clearInterval(interval);
      }
    }, 1000)
  };

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Update: The reason you are not seeing the correct value in your function is the way that setState happens(setTime). When you call setState, it batches the calls and performs them when it wants to in the background. So you cannot call setState then immediately expect to be able to use its value inside of the function.

You can Take the console.log out of the function and put it in the render method and you will see the correct value.

Or you can try useEffect like this.

//This means that anytime you use setTime and the component is updated, print the current value of time. Only do this when time changes.

useEffect(()=>{
   console.log(time);
},[time]);

Every time you setState you are rerendering the component which causes a havoc on state. So every second inside of your setInterval, you are re-rendering the component and starting it all over again ontop of what you already having running. To fix this, you need to use useEffect and pass in the state variables that you are using. I did an example for you here:

https://codesandbox.io/s/jolly-keller-qfwmx?file=/src/clock.js

import React, { useState, useEffect } from "react";
const Clock = (props) => {
  const [time, setTime] = useState(3);
  const [clockActive, setClockActive] = useState(false);

  useEffect(() => {
    let seconds = 60;

    setClockActive(true);
    const interval = setInterval(() => {
      setTime((time) => time - 1);
    }, 1000);

    if (time <= 0) {
      setClockActive(false);
      clearInterval(interval);
    }
    return () => {
      setClockActive(false);
      clearInterval(interval);
    };
  }, [time, clockActive]);

  return (
    <>
      {`Clock is currently ${clockActive === true ? "Active" : "Not Active"}`}
      <br />
      {`Time is ${time}`}
    </>
  );
};
export default Clock;

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...