this.state 与 useState 的 setTimeout [英] setTimeout for this.state vs useState
问题描述
当我使用类组件时,我有代码:
When I use class component, I have code:
setTimeout(() => console.log(this.state.count), 5000);
当我使用钩子时:
const [count, setCount] = useState(0);
setTimeout(() => console.log(count), 5000);
如果我触发setTimeout
,然后在超时(5000ms
)之前将count
更改为1,类组件将console.log(1)
(最新值),对于useState
,它是console.log(0)
(注册超时时的值).
为什么会发生这种情况?
If I trigger setTimeout
then change the count
to 1 before the timeout (5000ms
), class component will console.log(1)
(the newest value), and for useState
it is console.log(0)
(value when register timeout).
Why does this happen?
推荐答案
更新版本:
问题:setTimeout
/setInterval
内的 React State 变量在 function 和 中的行为差异>类组件?
Question: Difference in behavior of a React State variable inside setTimeout
/ setInterval
for function and class components?
案例 1:函数组件中的状态变量(过时的闭包):
Case 1: State variable in function component (stale closure):
const [value, setValue] = useState(0)
useEffect(() => {
const id = setInterval(() => {
// It will always print 0 even after we have changed the state (value)
// Reason: setInterval will create a closure with initial value i.e. 0
console.log(value)
}, 1000)
return () => {
clearInterval(id)
}
}, [])
案例 2:类组件中的状态变量(没有过时的闭包):
Case 2: State variable in class component (no stale closure):
constructor(props) {
super(props)
this.state = {
value: 0,
}
}
componentDidMount() {
this.id = setInterval(() => {
// It will always print current value from state
// Reason: setInterval will not create closure around "this"
// as "this" is a special object (refernce to instance)
console.log(this.state.value)
}, 1000)
}
案例 3:让我们尝试围绕 this
// Attempt 1
componentDidMount() {
const that = this // create a local variable so that setInterval can create closure
this.id = setInterval(() => {
console.log(that.state.value)
// This, too, always print current value from state
// Reason: setInterval could not create closure around "that"
// Conclusion: Oh! that is just a reference to this (attempt failed)
}, 1000)
}
案例 4:让我们再次尝试在类组件中创建一个陈旧的闭包
Case 4: Let's again try to create a stale closure in class component
// Attempt 2
componentDidMount() {
const that = { ...this } // create a local variable so that setInterval can create closure
this.id = setInterval(() => {
console.log(that.state.value)
// Great! This always prints 0 i.e. the initial value from state
// Reason: setInterval could create closure around "that"
// Conclusion: It did it because that no longer is a reference to this,
// it is just a new local variable which setInterval can close around
// (attempt successful)
}, 1000)
}
案例 5:让我们再次尝试在类组件中创建一个陈旧的闭包
Case 5: Let's again try to create a stale closure in class component
// Attempt 3
componentDidMount() {
const { value } = this.state // create a local variable so that setInterval can create closure
this.id = setInterval(() => {
console.log(value)
// Great! This always prints 0 i.e. the initial value from state
// Reason: setInterval created closure around value
// Conclusion: It is easy! value is just a local variable so it will be closed
// (attempt successful)
}, 1000)
}
案例 6:班级获胜(无需额外努力避免过时的关闭).但是,如何在函数组件中避免它?
Case 6: Class has won (no extra effort to avoid the stale closure). But, how to avoid it in function component?
// Let's find solution
const value = useRef(0)
useEffect(() => {
const id = setInterval(() => {
// It will always print the latest ref value
// Reason: We used ref which gives us something like an instance field.
// Conclusion: So, using ref is a solution
console.log(value.current)
}, 1000)
return () => {
clearInterval(id)
}
}, [])
案例 6:让我们为功能组件寻找另一种解决方案
Case 6: Let's find another solution for function components
useEffect(() => {
const id = setInterval(() => {
// It will always print the latest state value
// Reason: We used updater form of setState (which provides us latest state value)
// Conclusion: So, using updater form of setState is a solution
setValue((prevValue) => {
console.log(prevValue)
return prevValue
})
}, 1000)
return () => {
clearInterval(id)
}
}, [])
原始版本:
该问题是由闭包引起的,可以使用 ref
修复.但这里有一个解决方法来修复它,即使用updater"访问最新的 state
值setState
的形式:
The issue is caused by closures and can be fixed by using ref
. But here is a workaround to fix it i.e. access the latest state
value using "updater" form of setState
:
function App() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
setTimeout(() => console.log('count after 5 secs: ', count, 'Wrong'), 5000)
}, [])
React.useEffect(() => {
setTimeout(() => {
let count
setCount(p => {
console.log('p: ', p)
count = p
return p
})
console.log('count after 5 secs: ', count, 'Correct')
}, 5000);
}, [])
return (<div>
<button onClick={() => setCount(p => p+1)}>Click me before 5 secs</button>
<div>Latest count: {count}</div>
</div>)
}
ReactDOM.render(<App />, document.getElementById('mydiv'))
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<body>
<div id="mydiv"></div>
</body>
这篇关于this.state 与 useState 的 setTimeout的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!