为什么我的组件在setState之前呈现?

问题描述

编辑-添加render功能

我已经为此战斗了几周了。我想创建两个显示在仪表板中的列表,都使用API​​中的数据。如果我用虚数据创建一个const,则按要求显示列表。但是,当我尝试使用相同的代码显示来自我的API的信息时,在设置调用渲染的状态条件之前,会显示一个列表。有一次我没有显示任何列表,所以我为至少取得一些进展感到高兴。如果有人可以帮助指出我在做什么错,我将不胜感激!

async componentDidMount() {
    let data = {
      firstName: sessionStorage.getItem('cwsFirstName'),surname: sessionStorage.getItem('cwsSurname'),email: sessionStorage.getItem('cwsUser'),role: sessionStorage.getItem('cwsRole'),type: sessionStorage.getItem('cwsType'),storeId: sessionStorage.getItem('cwsstoreId'),clientId: sessionStorage.getItem('cwsClient')
    };

    let user = [];
    user.push(data);
    await this.setState({ user: user });
    const client = data.clientId;

    // start with extracting the services from the db
    const clientservices = await this.MysqLLayer.Get(`/admin/clientservices/${client}`);
    //console.log('clientservices: ',clientservices);

    let workzones = [
      {
        worklist: 'Queues',task: 'list_all'
      },{
        worklist: 'Today',task: 'list_today'
      }
    ];

    // loop through services to populate worklists
    let loopCount = 0;
    clientservices.forEach(async service => {
      let workspace = service.service;
      let type = service.type;

      let workspaces = [];

      // loop through the workzones
      workzones.forEach(async workzone => {
        //console.log('workzone: ',workzone);
        let worklists = [];
        let task = workzone.task;
        let records = await this.MysqLLayer.Get(`/${type}/${workspace}/${task}/${client}`);
        //console.log('records: ',records);

        let statusArr = [];
        records.forEach(async record => {
          //console.log('currentStatus: ',record.currentStatus);
          statusArr.push(record.currentStatus);
        });

        let completeWorklist = statusArr.filter(this.onlyUnique);

        let statusList = [];
        if (completeWorklist.length > 0) statusList = this.filterWorklists(workspace,completeWorklist);

        let items = [];
        statusList.forEach(async element => {
          //console.log('element: ',element);
          let count = 0;
          records.forEach(async record => {
            if (record.currentStatus === element) {
              ++count;
            }
          });
          items.push({
            item: element,count: count
          });
          //console.log('items: ',items);
        }); // end of worklist loop *******************************

        worklists.push({
          worklist: workzone.worklist,items: items
        });
        //console.log('worklists: ',workzone,worklists,moment(new Date()).format('HH:mm:sss'));
        //console.log('worklists: ',moment(new Date()).milliseconds());

        await this.setState({
          records: records,type: type,worklists: [...this.state.worklists,...worklists]
        });

        ++loopCount;
        console.log('loopCount: ',loopCount);

        //console.log('this.state.worklists: ',this.state.worklists,moment(new Date()).milliseconds());
        let tempWorklists = this.state.worklists;
        //console.log('tempWorklists: ',tempWorklists,moment(new Date()).milliseconds());

        workspaces.push({
          workspace: workspace,worklists: tempWorklists
        });


      }); // end of workzone loop *******************************


      //console.log('2 workspaces: ',workspaces);

      await this.setState({
        //loading: false,//workspaces: [...this.state.workspaces,...workspaces]
        workspaces: workspaces
        //workspaces: workspacesConst
      });
      //console.log('this.state.workspaces: ',this.state.workspaces,moment(new Date()).milliseconds());

      await this.setState({
        //loading: false,moment(new Date()).milliseconds());

    }); // end of workspace loop *******************************

    // Should be good to render Now
    //if (this.state.worklists.length > 1)
    await this.setState({ loading: false });
    //this.forceUpate();

  }

render() {

    if (this.state.loading) {
      return (
        <div>Loading...</div>
      );
    } else {

      const workspace = this.state.workspaces.map((workspace,idx) =>
      //const workspace = workspaces.map((workspace,idx) =>
        <div key={idx} className="card border-light mb-3" style={{padding: "20px"}}>
          {console.log('render workspace: ',workspace.worklists,moment(new Date()).milliseconds())}
          <Workspace
            key={idx}
            records={this.state.records}
            workspaces={workspace}
            type={this.state.type}
            user={this.state.user}
          />
        </div>
      );

      return (
        <div className="container">
          <div className="cols-12">
            <Welcome user={this.state.user}/>
            {workspace}
          </div>
        </div>
      );
    }
  }

Render console log

如您所见,渲染发生在this.state.loading设置为'false'之前。当然应该只在那之后渲染吗?

解决方法

不,这不是setState和生命周期反应的方式。
实际上,您每次调用setState时都会自动调用render方法。 因此,在您的componentDidMount中,您多次调用setState。因此它将多次渲染您的组件。并渲染您的工作区。
直到您最后一次调用setState来设置loading false时,它才会停止。
我建议您使用spinner中的progressbar或类似的内容在loadingtrue时显示,一旦变成false,您可以显示{{1} }。
还有一个需要注意的地方是workspacessetState,但是asynchronous不能与await一起使用,因为它不会返回任何setState
有关反应生命周期的更多信息-

,

这里有两个问题导致了我的问题:

    正如Sagar指出的,
  1. awaitsetState不兼容-我以前不知道这一点
  2. 具有返回Promise功能的
  3. await确实可以正常工作(例如API调用),但是代码将继续运行直到Promise实现为止。我知道这一点,但还没有意识到这对我尝试创建的序列意味着什么。

我通过在代码的每一行中添加一个console.log来弄清楚这一点,以查看何时发生了什么。我开始意识到,要求提取API的代码行是导致问题的原因:

let records = await this.mysqlLayer.Get(`/${type}/${workspace}/${task}/${client}`);

我的代码一直在运行,并更新状态以表示已完成,因为await意味着它正在并行运行各节。

我还没有解决该代码的问题,但是想让其他人知道类似的问题。