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

标签 reactjs state react-hooks

在之前的 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 中得出实际需要发生的操作。

在基于钩子(Hook)的函数组件中我该如何做?

<小时/>

更好的用例是一个有两个按钮的屏幕,一个用于加载上一个按钮,另一个用于加载下一个按钮。当 isLoading 状态为 true 时,按钮将被禁用,并且仅在加载时执行操作。为了确保只发生单个负载,我们不能只是“行动”就好像状态立即更改一样:状态更改是异步的,因此可能会发生竞争条件。

在更改状态时使用回调可以防止这种竞争情况并确保只发生一次加载:

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。像这样的自定义 Hook 可能会起作用:

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

  ...
}

关于reactjs - 在 useState 环境中模拟 setState 的第二个参数。让 setState 发生后发生一些事情,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59965287/

相关文章:

javascript - 有没有办法检查延迟加载的组件(使用 React.Lazy)是否已完成加载?

javascript - 如何在不重新触发 useEffect 的情况下访问 useEffect 中的状态?

css - React js 旋转木马箭头/位置

flutter - 送餐应用 : bloc or Provider which one to use?

javascript - React-按钮功能

javascript - Redux 状态在窗口重新加载时重置(客户端)

reactjs - React Hooks,如何实现useHideOnScroll hook?

javascript - 使用 useState Hook 时 - 更改 setState 函数调用顺序是否重要?

reactjs - 聚光灯不工作,而半球光和点光在我的代码中工作

javascript - 如何在 JSX 中添加三元运算符?