React hooks:获取当前状态,返回未来
这篇文章虽然不是最近才写的,但仍然很有价值!而且我仍然经常看到开发者为此感到困扰。我在我的新电子书《React 开发者 Hooks 指南》中讨论了这个话题以及更多关于 React Hooks 的内容。
React Hooks确实很棒,但是我玩得越多,就越发现其中的技巧,有时我会花很多时间来弄清楚为什么我的代码没有按照预期运行。
我的最后一个问题是:我想从异步触发的回调(例如在或中)访问useState
组件的当前状态(用创建)。useEffect
useCallback
下面是一个可能未按预期运行的代码示例:
const Counter = () => {
const [counter, setCounter] = useState(0)
const onButtonClick = useCallback(() => setCounter(counter + 1), [counter])
const onAlertButtonClick = useCallback(
() => {
setTimeout(() => {
alert('Value: ' + counter)
}, 5000)
},
[counter]
)
return (
<div>
<p>You clicked {counter} times.</p>
<button onClick={onButtonClick}>Click me</button>
<button onClick={onAlertButtonClick}>
Show me the value in 5 seconds
</button>
</div>
)
}
你可能认出从React 文档中提取的计数器示例,我在其中添加了一个新按钮。点击此按钮后,五秒钟后会显示一个 alert,其中包含计数器的当前值。或者说,你可以想象,但不幸的是,显示的值并不是当前值。
假设您在计数器为 5 时点击了按钮,然后立即点击了三次递增按钮。您期望警报显示 8,但它显示的是 5。这是因为在传递给 的函数中setTimeout
,counter
的值是 5,并且没有理由更新它(React Hooks 没有那么神奇)。这纯粹是 JavaScript 闭包和作用域的问题,所以显然我们需要找到另一种方法来实现我们想要的效果。
答案是:refs和 hooks useRef
。我们的想法是使用 ref 作为计数器;每次 时它都会更新counter
,然后我们会在传给 的函数中使用它的当前值setTimeout
。
因此,首先我们声明我们的 ref,并使用当前计数器值作为初始值:
const counterRef = useRef(counter)
然后我们希望每次counter
更新时都更新它,因此我们可以使用useEffect
:
useEffect(
() => { counterRef.current = counter },
[counter]
)
最后,我们只需要使用counterRef.current
超时函数:
const onAlertButtonClick = useCallback(() => {
setTimeout(() => {
alert('Value: ' + counterRef.current)
}, 5000)
}, [])
注意:我认为没有必要给出[counter]
第二个参数,因为counterRef
在渲染之间不应该改变。
效果非常好!我们甚至可以创建一个自定义钩子,使这个过程更简单、更可重用:
const useRefState = initialValue => {
const [state, setState] = useState(initialValue)
const stateRef = useRef(state)
useEffect(
() => { stateRef.current = state },
[state]
)
return [state, stateRef, setState]
}
我们的组件代码就变得简单多了:
const Counter = () => {
const [counter, counterRef, setCounter] = useRefState(0)
const onButtonClick = useCallback(() => setCounter(counter + 1), [counter])
const onAlertButtonClick = useCallback(() => {
setTimeout(() => {
alert('Value: ' + counterRef.current)
}, 5000)
}, [])
return (
<div>
<p>You clicked {counter} times.</p>
<button onClick={onButtonClick}>Click me</button>
<button onClick={onAlertButtonClick}>
Show me the value in 5 seconds
</button>
</div>
)
}
我不完全确定这是解决未来获取状态值问题的最佳方法,尽管它看起来运行良好。您在使用状态和钩子时遇到过同样的问题吗?您认为还有其他方法吗?或者这种方法有什么问题吗?
本文最初发布在我的博客上。照片由Sergey Zolkin 在 Unsplash 上拍摄。
鏂囩珷鏉ユ簮锛�https://dev.to/scastiel/react-hooks-get-the-current-state-back-to-the-future-3op2