Pei*_*ong 11 javascript reactjs react-hooks
代码在这里: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>;
};
Run Code Online (Sandbox Code Playgroud)
问题是每次setCounter
触发重新渲染,因此间隔重置并重新创建.这可能看起来很好,因为状态(计数器)保持递增,但是当与其他挂钩组合时它可能会冻结.
这样做的正确方法是什么?在类组件中,使用保持间隔的实例变量很简单.
Tho*_*lle 17
您希望将一个空数组作为第二个参数,useEffect
以便该函数仅在初始渲染后运行一次.
由于闭包的工作方式,这将使counter
变量始终引用初始值.您可以使用函数版本setCounter
来始终获取正确的值.
例
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')
);
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)
正确的方法是只运行一次效果。由于您只需要运行一次效果,因为在挂载期间,您可以传入一个空数组作为第二个参数来实现。
但是,您需要更改setCounter
以使用 的先前值counter
。原因是因为传入setInterval
的闭包的回调只访问counter
第一次渲染中的变量,它无法访问counter
后续渲染中的新值,因为useEffect()
第二次没有调用;回调中counter
的值始终为 0 setInterval
。
就像setState
你所熟悉的那样,状态钩子有两种形式:一种是接收更新状态的形式,另一种是传入当前状态的回调形式。你应该使用第二种形式并读取setState
回调中的最新状态值来在增加它之前确保你有最新的状态值。
function Counter() {
const [counter, setCounter] = React.useState(0);
React.useEffect(() => {
const timer = setInterval(() => {
setCounter(prevCount => prevCount + 1); // <-- Change this line!
}, 1000);
return () => {
clearInterval(timer);
};
}, []); // Pass in empty array to run effect only once!
return (
<div>Count: {counter}</div>
);
}
ReactDOM.render(<Counter />, document.querySelector('#app'));
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)
正如@YangshunTay 的另一个答案已经表明的那样,可以让它useEffect
回调只运行一次并与componentDidMount
. 在这种情况下,由于函数作用域的限制,有必要使用状态更新器函数,否则counter
在setInterval
回调中更新将不可用。
另一种方法是useEffect
在每次计数器更新时运行回调。在这种情况下setInterval
应替换为setTimeout
,并且更新应仅限于counter
更新:
export default ({ name }: Props) => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const timeout = setTimeout(() => {
setCounter(counter + 1);
}, 1000);
return () => {
clearTimeout(timeout);
};
}, [counter]);
return <h1>{counter}</h1>;
};
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
5813 次 |
最近记录: |