这里的代码是:https://codesandbox.io/s/nw4jym4n0

export default ({ name }: Props) => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter(counter + 1);
    }, 1000);

    return () => {
      clearInterval(interval);
    };
  });

  return <h1>{counter}</h1>;
};

问题是每setCounter个触发器都会重新渲染,因此间隔会被重置并重新创建.这看起来很好,因为状态(计数器)一直在增加,但是当与其他挂钩组合时,它可能会冻结.

正确的方法是什么?在类组件中,使用一个实例变量来保持间隔很简单.

推荐答案

可以将空数组作为第二个参数指定给useEffect,这样函数在初始渲染后只运行一次.由于闭包的工作方式,这将使counter变量始终引用初始值.然后可以使用setCounter的函数版本来始终获得正确的值.

Example

const { useState, useEffect } = React;

function App() {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter(counter => counter + 1);
    }, 1000);

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

  return <h1>{counter}</h1>;
};

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
<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="root"></div>

一种更通用的方法是创建一个新的自定义钩子,将函数存储在ref中,并且仅在delay发生变化时创建一个新的间隔,like Dan Abramov does in his great blog post "Making setInterval Declarative with React Hooks".

Example

const { useState, useEffect, useRef } = React;

function useInterval(callback, delay) {
  const savedCallback = useRef();

  // Remember the latest callback.
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // Set up the interval.
  useEffect(() => {
    let id = setInterval(() => {
      savedCallback.current();
    }, delay);
    return () => clearInterval(id);
  }, [delay]);
}

function App() {
  const [counter, setCounter] = useState(0);

  useInterval(() => {
    setCounter(counter + 1);
  }, 1000);

  return <h1>{counter}</h1>;
};

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
<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="root"></div>

Reactjs相关问答推荐

Shadck/ui Select -当用户 Select 项目时更改状态

为什么安装FLOBIT后,所有的输入FIELD现在都有一个蓝色的轮廓?

如何在重新图表中更改悬停时的条形填充 colored颜色 ?

带有样式的工具提示导致无法向功能组件提供参考警告

根据处于react 状态的数组的名称键,将新对象添加到嵌套的对象数组中

MUI 日期 Select 器 - 最小日期混乱

组件安装后调用自定义钩子

如何根据用户在react.js中的 Select , Select 多个心形图标?

每次状态更改时都会提交react 表单

单击按钮时如何添加或删除 3d 对象

React - 添加了输入数据但不显示

页面不显示

来自一个自定义 React Native 组件的样式从另一个自定义组件移除样式

当我在 useEffect 中使用 useDispatch 时,我的组件继续渲染

react-markdown 不渲染 Markdown

当状态改变时如何执行一些动作,但只针对第一次更新?

在表格中显示具有自定义声明的用户状态

如何使用 babel 将 SVG 转换为 React 组件?

为什么重新渲染不会影响 setTimeout 的计数?

如何判断对话框组件是否位于对话框之上?