React setState and await

我的梦境 提交于 2021-02-11 14:34:54

问题


While the functional setState() is recommended when the current state is being used, the function still cannot be async. How could we develop a function that uses the state, calls a REST API and changes the state right after?

This is the original function, that disregards the async behavior of 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 });
}

If we change the above function to use functional setState, VSCode complains that await can only be used in async functions.

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

I think I'm missing something fundamental here.


回答1:


Short answer

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

Long answer

Why use setState callbacks

The React docs state that multiple calls to setState may be batched together.

Let's pretend you have a chat app. You might have a function that looks like this:

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

So if 2 messages come in a few milliseconds apart, React may batch these two calls to setState()

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

and when 2 runs, we will lose message_1.

Providing setState with a transform function is the solution

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

In the callback-based setState, the 1st message will not be lost, since instead of specifying an exact state to write, we specify a way to transform the old state into the new state.

Important notes

The object version of setState() does a shallow merge of the new state and the previous state. This means that calling setState() with an object will only update the keys that you pass in.

If you choose to use the callback version of setState() instead, you are expected to return the entire new state. So when you have

return { params: searchParams, result: result };

in your setState() callback, you will lose all state value except params and result.

Your code

the functional setState() is recommended when the current state is being used

It is important to understand why the functional setState() is recommended. If your code will not break when multiple setState() calls are batched together, it is OK to pass an object instead of a function.

If you still want to use the callback pattern, you don't need to do all the work in your setState() callback, you only need to specify how to transform the old state into the new one.

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



回答2:


Think of 'await' as a callback function of promise. So you don't have promise here and you are calling callback function. Hence, VSCode ask you to add the promise which in this case it is 'async' function.



来源:https://stackoverflow.com/questions/49216135/react-setstate-and-await

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!