使用 React hook 实现自增计数器 [英] Use React hook to implement a self-increment counter
问题描述
代码在这里: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>
一种更通用的方法是创建一个新的自定义钩子,将函数存储在 ref
并且只有在 delay
应该改变时才创建一个新的间隔,就像 Dan Abramov 在他伟大的博客文章中所做的"Making setInterval Declarative使用 React Hooks".
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 hook 实现自增计数器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!