React setState和await

时间:2018-03-11 02:44:20

标签: reactjs async-await setstate

虽然在使用当前状态时建议使用函数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 };
    });
}

我想我在这里缺少一些基本的东西。

2 个答案:

答案 0 :(得分:2)

简短回答

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)})
}

因此,如果两条消息相隔几毫秒,则React可以将这两个调用批处理为setState()

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

2投放时,我们将失去message_1

为setState提供转换函数是解决方案

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,
        }) 
    })
}

答案 1 :(得分:1)

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