【问题标题】:React setState and await反应 setState 并等待
【发布时间】:2018-08-19 08:18:16
【问题描述】:

虽然在使用当前状态时建议使用函数 setState(),但该函数仍然不能异步。我们如何开发一个使用状态、调用 REST API 并立即更改状态的函数?

这是原始函数,它忽略了 setState 的异步行为。

onSortColumnChanged = (sortColumn) => async (event) => {
    const prev = this.state;
​    const searchParams = { ...prev.params, sortColumn: sortColumn };
​    const result = await this.callSearch(searchParams);
​    this.setState({ params: searchParams, result: result });
}

如果我们把上面的函数改成使用函数式 setState,VSCode 会抱怨 await 只能用在异步函数中。

onSortColumnChanged = (sortColumn) => (event) => {
    this.setState((prev) => {
​        const searchParams = { ...prev.params, sortColumn: sortColumn };
​        const result = await this.callSearch(searchParams);
​        return { params: searchParams, result: result };
    });
}

我想我在这里遗漏了一些基本的东西。

【问题讨论】:

  • 我想你只需要把this.setState((prev) => {改成this.setState(async (prev) => {
  • @Tony setState 不会解决承诺

标签: reactjs async-await setstate


【解决方案1】:

简答

onSortColumnChanged = (sortColumn) => async (event) => {
    const searchParams = { ...this.state.params, sortColumn: sortColumn };
    const result = await this.callSearch(searchParams);
    this.setState(prev => {
        prev.params.sortColumn = sortColumn
        prev.result = result
        return prev
    })
}

长答案

为什么使用 setState 回调

The React docs 声明对 setState 的多次调用可能会被批处理。

假设您有一个聊天应用程序。你可能有一个看起来像这样的函数:

async incoming_message(message){
    this.setState({messages: this.state.messages.concat(message)})
}

因此,如果 2 条消息相隔几毫秒,React 可能会将这两个调用批处理到 setState()

this.setState({messages: [].concat(message_1)}) // 1
this.setState({messages: [].concat(message_2)}) // 2

2运行时,我们将失去message_1

给setState提供一个transform函数就是解决办法

async incoming_message(message){
    this.setState(prev => {
        prev.messages.push(message)
        return prev
    })
}

在基于回调的 setState 中,第一条消息不会丢失,因为我们没有指定要写入的确切状态,而是指定了一种将旧状态转换为新状态的方法。

重要提示

setState() 的对象版本对新状态和先前状态进行了浅合并。这意味着使用对象调用 setState()更新您传入的键。

如果您选择使用setState() 的回调版本,您应该返回整个新状态。所以当你有

return { params: searchParams, result: result };

在您的setState() 回调中,您将丢失除paramsresult 之外的所有状态值。

您的代码

在使用当前状态时建议使用函数 setState()

了解为什么建议使用功能性setState() 很重要。如果多个setState() 调用一起批处理时您的代码不会中断,则可以传递对象而不是函数。

如果你仍然想使用回调模式,你不需要在你的setState()回调中做所有的工作,你只需要指定如何将旧状态转换为新状态。

onSortColumnChanged = (sortColumn) => async (event) => {
    const {params} = this.state;
    const searchParams = { ...params, sortColumn: sortColumn };
    const result = await this.callSearch(searchParams);
    this.setState(prev => {
        // prev is the old state
        // we changed sortColumn param
        prev.params.sortColumn = sortColumn
        // and we changed the result
        prev.result = result
        // and we return the new, valid state
        return prev

        // or if you prefer an immutable approach
        return Object.assign({}, prev, {
            params: {...prev.params, sortColumn: sortColumn},
            result: result,
        }) 
    })
}

【讨论】:

  • 感谢您的完整解释。不过我还有一个问题。搜索参数可以由其他处理程序更改,例如this.state.params.filters 可以由onFilterChanged 更改。使用您的最后一个代码,当执行const {params} = this.state 时,我们可能会收到过时的过滤器,因此搜索不会提供准确的结果。假设onFilterChanged 的实现类似,我们最终可能会得到错误过滤器或错误排序列的结果。
  • @GabrielaVasselai const {params} 令人困惑,我刚刚修复了一些其他错误。
  • @GabrielaVasselai @GabrielaVasselai 如果我们在setState 回调中使用params,我们可能会创建不正确的状态。但是,setState 回调修改 state.resultstate.params.searchParams,因此如果同时在其他地方修改 state.filters 将是安全的。
【解决方案2】:

把'await'想象成一个promise的回调函数。所以你在这里没有承诺,你正在调用回调函数。因此,VSCode 要求您添加承诺,在这种情况下它是“异步”功能。

【讨论】:

    猜你喜欢
    • 2019-10-21
    • 1970-01-01
    • 1970-01-01
    • 2021-12-30
    • 2020-10-16
    • 2015-03-24
    • 2021-06-27
    • 2019-10-29
    • 2019-08-22
    相关资源
    最近更新 更多