首页 > 解决方案 > 如何在 Reactjs 16.7.0 中使用异步服务器调用

问题描述

在 ReactJs 中使用异步方法作为道具的正确方法是什么?我有一个安装组件“仪表板”的应用程序,然后该组件加载两个下拉菜单。第一个下拉列表由生命周期方法fetch中的异步填充。componentDidMount

当第一个下拉列表更改时,将触发异步事件处理程序并且selectedAId应该设置 state 属性。如果我在浏览器中调试它,我会看到这个状态道具集。设置状态后,相同的事件处理程序还调用异步服务器方法以基于 state 属性获取一些数据selectedAId

如果我然后转到异步服务器方法getEntitledSites,则发送给该方法的参数是“0”,而不是我之前看到的在事件处理程序中设置的值。

以下是代码摘要的要点:

https://gist.github.com/thehme/c4b5a958ef1a6e5248f697375c9cb84b#file-api-ts-L15

api.ts

class Api {
...

async getEntitledForA() {
    try {
        const orgsResponse = await fetch('/api/v1/orgs');
        const orgResponseJson = await orgsResponse.json();
        return orgResponseJson.data;
    } catch (err) {
        console.error(err);
        return null;
    }
}

async getEntitledSites(orgId: number) {
    try {
        const sitesResponse = await fetch('/api/v1/study/' + orgId + '/sites');
        const sitesResponseJson = await sitesResponse.json();
        return sitesResponseJson;
    } catch (err) {
        console.error(err);
        return null;
    }
}
}

export default new Api();

仪表板.jsx

import React, {Component} from 'react';
import SelectDropDownA from '../components/SelectDropDownA';
import SelectDropDownB from '../components/SelectDropDownB';
import SelectDropDownC from '../components/SelectDropDownC';

class CreateDashboard extends Component {
    constructor() {
        super();

        this.state = {
            organizations: [],
            sites: [],
            selectedAId: "0",
            selectedBId: "0",
            selectedCId: "0",
            showBMenu: false
        }

    }

    async componentDidMount() {
        let organizations = await Api.getEntitledForA();
        this.setState({ organizations });
    }

    async onSelectedOrgChange(selectedOrgId) {
        if (selectedOrgId !== 0) { 
            this.setState({ selectedAId });
            let sitesData = await Api.getEntitledSites(this.state.selectedAId);
            this.setState({ 
                sites: sitesData.sites,
                showSitesMenu: true
            });
        }
    }

    ...


    render() {
        return (
            <div className="panel">
                <div className="insidePanel">
                    <SelectStudyDropDown 
                        onChange={e => this.onSelectedOrgChange(e.target.value)}
                        ...
                    />
                </div>
                {this.showSitesMenu && 
                    <div className="insidePanel">
                        <SelectSiteDropDown
                            onChange={e => this.onSelectedSiteChange(e.target.value)}
                            ...
                        />
                    </div>
                }
            </div>
        )
    }
}

export default CreateDashboard;

我想知道这是否与我的使用方式async/await或方法的绑定有关onChange

解决方案:在我的特殊情况下,我不需要在启动服务器请求之前this.state进行设置selectedAId,因此通过将参数直接传递给请求,可以正常工作。然后我可以简单地将 的设置移动selectedAIdthis.setState. 如果我需要selectedAId在发出服务器请求之前进行设置,这是行不通的,在这种情况下,我必须等待this.state完成设置,但事实并非如此,因为我希望我的服务器请求立即开始。

async onSelectedOrgChange(selectedOrgId) {
    if (selectedOrgId !== 0) { 
        let sitesData = await Api.getEntitledSites(selectedOrgId);
        this.setState({ 
            selectedAId,
            sites: sitesData.sites,
            showSitesMenu: true
        });
    }
}

标签: reactjsasync-await

解决方案


this.setState是异步且不可等待的(即不返回承诺),因此如果您想在设置新状态后对其执行某些操作,则需要在回调中执行此操作:

this.setState({ selectedAId }, async () => {
    let sitesData = await Api.getEntitledSites(this.state.selectedAId);
    this.setState({ 
        sites: sitesData.sites,
        showSitesMenu: true
    });
});

此外,在您的示例中,onSelectedOrgChange未绑定到this任何地方。但是,如果这是一个问题,那么整个事情很久以前就会坏掉,所以我想你只是不小心把它漏掉了?


此外,我建议您提取e.target.value处理程序内部并作为道具传递onChange={this.onSelectedOrgChange},因为在您当前的代码中,onChange每次调用 render 时都会创建一个新的处理程序。


推荐阅读