在 useState 环境中模拟 setState 的第二个参数。让 setState 发生后发生一些事情

pau*_*l23 5 state reactjs react-hooks

在之前的 React 版本中,我们可以在状态更改后执行代码,方法如下:

setState( prevState => {myval: !prevState.myval}, () => {
    console.log("myval is changed now");
)
Run Code Online (Sandbox Code Playgroud)

并确保第二个“位”仅使用更新后的代码执行。现在,其中一个示例描述了可以使用以下方法实现“类似”的效果React.useEffect

const [myval, setMyval] = React.useState(false);
React.useEffect(() => {
    console.log("myval is changed now");
}

//somewhere
setMyval(o => !o);
Run Code Online (Sandbox Code Playgroud)

虽然这看起来不错,但对我来说没有帮助:在我的情况下,“需要发生什么效果”取决于状态发生变化的位置,最初:

//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");
)
Run Code Online (Sandbox Code Playgroud)

将它们更改为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>
    }
}
Run Code Online (Sandbox Code Playgroud)

Ros*_*len 1

问题“是否是正在执行的异步操作”是“是否有任何正在执行的异步操作”的减少,因此您可以分别对每个异步操作进行建模,然后计算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

  ...
}
Run Code Online (Sandbox Code Playgroud)