我正在尝试新的React钩子,并有一个时钟组件,它的时间值应该每秒钟增加。但是,该值不会超过1。

function Clock() { const [time, setTime] = React.useState(0); React.useEffect(() => { const timer = window.setInterval(() => { setTime(time + 1); }, 1000); return () => { window.clearInterval(timer); }; }, []); return ( <div>Seconds: {time}</div> ); } ReactDOM.render(<Clock />, document.querySelector('#app')); <script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script> <div id="app"></div>


当前回答

在某种程度上类似的问题,但当使用状态值时,状态值是一个对象,并且不更新。

我有一些问题,所以我希望这可以帮助到一些人。 我们需要传递与新对象合并的旧对象

const [data, setData] = useState({key1: "val", key2: "val"});
useEffect(() => {
  setData(...data, {key2: "new val", newKey: "another new"}); // --> Pass old object
}, []);

其他回答

原因是传递给setInterval闭包的回调只访问第一次呈现中的时间变量,它不能访问后续呈现中的新时间值,因为第二次没有调用useEffect()。

在setInterval回调函数中,time的值总是0。

与您熟悉的setState一样,状态钩子有两种形式:一种是它接收更新状态的形式,另一种是传递当前状态的回调形式。您应该使用第二种形式,并在setState回调中读取最新的状态值,以确保在增加状态值之前拥有最新的状态值。

好处:可选择的方法 Dan Abramov在他的博客文章中深入讨论了关于使用setInterval和钩子的话题,并提供了解决这个问题的替代方法。强烈推荐阅读!

function Clock() { const [time, setTime] = React.useState(0); React.useEffect(() => { const timer = window.setInterval(() => { setTime(prevTime => prevTime + 1); // <-- Change this line! }, 1000); return () => { window.clearInterval(timer); }; }, []); return ( <div>Seconds: {time}</div> ); } ReactDOM.render(<Clock />, document.querySelector('#app')); <script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script> <div id="app"></div>

  const [loop, setLoop] = useState(0);
  
  useEffect(() => {
    setInterval(() => setLoop(Math.random()), 5000);
  }, []);

  useEffect(() => {
    // DO SOMETHING...
  }, [loop])

另一种解决方案是使用useReducer,因为它总是被传递当前状态。

function Clock() { const [time, dispatch] = React.useReducer((state = 0, action) => { if (action.type === 'add') return state + 1 return state }); React.useEffect(() => { const timer = window.setInterval(() => { dispatch({ type: 'add' }); }, 1000); return () => { window.clearInterval(timer); }; }, []); return ( <div>Seconds: {time}</div> ); } ReactDOM.render(<Clock />, document.querySelector('#app')); <script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script> <div id="app"></div>

useRef可以解决这个问题,这里有一个类似的组件,每1000毫秒增加计数器

import { useState, useEffect, useRef } from "react";

export default function App() {
  const initalState = 0;
  const [count, setCount] = useState(initalState);
  const counterRef = useRef(initalState);

  useEffect(() => {
    counterRef.current = count;
  })

  useEffect(() => {
    setInterval(() => {
      setCount(counterRef.current + 1);
    }, 1000);
  }, []);

  return (
    <div className="App">
      <h1>The current count is:</h1>
      <h2>{count}</h2>
    </div>
  );
}

我认为这篇文章将帮助您使用interval react钩子

当提供空输入列表时,useEffect函数只在组件挂载时计算一次。

setInterval的另一个替代方法是在每次状态更新时使用setTimeout设置新的间隔时间:

  const [time, setTime] = React.useState(0);
  React.useEffect(() => {
    const timer = setTimeout(() => {
      setTime(time + 1);
    }, 1000);
    return () => {
      clearTimeout(timer);
    };
  }, [time]);

setTimeout对性能的影响很小,一般可以忽略。除非组件对时间敏感,以至于新设置的超时会导致不良影响,否则setInterval和setTimeout方法都是可以接受的。