在以前的 react 版本中,我们可以通过执行以下操作在状态更改后执行代码:
setState( prevState => {myval: !prevState.myval}, () => {
console.log("myval is changed now");
)
并确保第二个"位"仅使用更新的代码执行。现在,其中一个示例描述了可以使用React.useEffect
实现"类似"的东西:
const [myval, setMyval] = React.useState(false);
React.useEffect(() => {
console.log("myval is changed now");
}
//somewhere
setMyval(o => !o);
虽然这看起来很好,但在我的情况下没有帮助:在我的情况下,"需要发生什么效果"取决于状态更改的位置,最初:
//somewhere
setState( prevState => {myval: !prevState.myval}, () => {
console.log("myval is changed now");
)
//somewhere else
setState( prevState => {myval: !prevState.myval}, () => {
console.log("myval has changed somewhere else");
)
将它们更改为useState
+setMyval
将使相同的效果在两个位置触发,因此我们无法在 useEffect 中推导出实际需要发生的操作。
在基于钩子的函数组件中,我将如何执行上述操作?
更好的用例是具有两个按钮的屏幕,一个用于加载上一个按钮,另一个用于加载下一个按钮。当状态为 true 时isLoading
按钮处于禁用状态,并且仅在加载时执行操作。为了确保只能发生单个负载,我们不能只是"行动",就好像状态立即更改一样:状态更改是异步的,因此可能会发生争用条件。
在更改状态时使用回调可防止此争用条件,并确保仅发生单个负载:
class SomScreen extends React.component {
state = { isLoading: false }
render() {
<div>
<button
disabled={this.state.isLoading}
onclick={(e) => {
if (!this.state.isLoading) {
this.setState((p) => ({isLoading: true}), () => {
await fetchPreviousDataAndDoSomethingWithIt()
setState({isLoading: false});
});
}
}
/>Previous</button>
<button
disabled={this.state.isLoading}
onclick={(e) => {
if (!this.state.isLoading) {
this.setState((p) => ({isLoading: true}), () => {
await fetchNextDataAndDoSomethingWithIt()
setState({isLoading: false});
});
}
}
/>Next</button>
</div>
}
}
问题"是飞行中的异步操作"是"这些异步操作中的任何一个在飞行中"的简化,因此您可以单独对每个异步操作进行建模,然后计算isLoading
。像这样的自定义钩子可能会起作用:
function useAsyncAction(asyncAction) {
const [isLoading, setIsLoading] = useState(false);
const callAsyncAction = useCallback(async () => {
setIsLoading(true);
await asyncAction();
setIsLoading(false);
}, []);
return [
isLoading,
callAsyncAction,
]
}
//
function SomScreen() {
const [asyncAIsLoading, callAsyncA] =
useAsyncAction(fetchPreviousDataAndDoSomethingWithIt);
const [asyncBIsLoading, callAsyncB] =
useAsyncAction(fetchNextDataAndDoSomethingWithIt);
const isLoading = asyncAIsLoading || asyncBIsLoading
...
}