使用React钩子实现一个自增计数器 [英] Use React hook to implement a self-increment counter

查看:26
本文介绍了使用React钩子实现一个自增计数器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

代码在这里: 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 触发器都会重新渲染,因此间隔会被重置并重新创建.由于状态(计数器)一直在增加,所以这看起来似乎不错,但是当与其他钩子结合使用时,它可能会冻结.

The problem is each setCounter trigger re-rendering so the interval got reset and re-created. This might looks fine since the state(counter) keeps incrementing, however it could freeze when combining with other hooks.

执行此操作的正确方法是什么?在类组件中,使用持有间隔的实例变量很简单.

What's the correct way to do this? In class component it's simple with a instance variable holding the interval.

推荐答案

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

You could give an empty array as second argument to useEffect so that the function is only run once after the initial render. Because of how closures work, this will make the counter variable always reference the initial value. You could then use the function version of setCounter instead to always get the correct value.

示例

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>

一种更通用的方法是创建一个新的自定义钩子,将该钩子存储在 .

A more versatile approach would be to create a new custom hook that stores the function in a ref and only creates a new interval if the delay should change, like Dan Abramov does in his great blog post "Making setInterval Declarative with React Hooks".

示例

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>

这篇关于使用React钩子实现一个自增计数器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆