如何在Reactjs 16.7.0中使用异步服务器调用

时间:2019-02-06 20:08:37

标签: reactjs async-await

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

当第一个下拉列表更改时,将触发异步事件处理程序,并应设置状态属性selectedAId。如果我在浏览器中调试它,我会看到此状态属性集。设置状态后,同一事件处理程序还会调用一个异步服务器方法来根据状态属性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();

dashboard.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来启动服务器请求,因此通过将参数直接传递给请求即可精细。然后,我可以简单地将selectedAId的设置移动到this.setState的第二个实例。如果在发出服务器请求之前需要设置selectedAId,这将无法正常工作,在这种情况下,我必须等待this.state完成设置,但事实并非如此。我希望我的服务器请求立即开始。

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

1 个答案:

答案 0 :(得分:0)

this.setState是异步的并且不等待(即不返回promise),因此,如果要在设置新状态后对新状态进行操作,则需要在回调中完成该操作:

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处理程序被称为。