【问题标题】:Why does my component render before setState?为什么我的组件在 setState 之前渲染?
【发布时间】:2020-08-20 03:01:43
【问题描述】:

编辑 - 添加 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: ', workzone, 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,
        //workspaces: [...this.state.workspaces, ...workspaces]
        workspaces: workspaces
        //workspaces: workspacesConst
      });
      //console.log('this.state.workspaces: ', this.state.workspaces, 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>
      );
    }
  }

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

【问题讨论】:

  • 一旦您使用 setState 更新状态,渲染将始终执行。在第一次迭代之后,您将更新工作区的状态,这将强制重新渲染。看不到渲染功能就忍不住了。
  • 抱歉,我忘了包括render。我现在编辑了我的问题以添加它。

标签: reactjs render setstate


【解决方案1】:

不,这不是 setState 和 react 生命周期的工作方式。
实际上每次调用 setState 时,react 都会自动调用 render 方法。 因此,在您的 componentDidMount 中,您多次调用 setState。所以它会多次渲染你的组件。并渲染您的工作区。
在您最后一次调用 setState 将加载设置为 false 之前,它不会停止。
我建议你使用spinnerprogressbar 或类似的东西来显示,而loadingtrue,一旦它变成false,你就可以显示workspaces
这里还有一个注意事项setStateasynchronousawait 不能与setState 一起使用,因为它不返回任何promise
更多关于反应生命周期 -

【讨论】:

  • 老实说,我不知道 await 不能与 setState 一起使用,但现在我知道了,这对我来说更有意义。
【解决方案2】:

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

  1. 正如 Sagar 所指出的,await 不适用于 setState - 我以前不知道这一点
  2. await 带有返回 Promise 的函数确实可以工作(例如 API 调用),但代码将继续运行,直到 Promise 完成。我知道这一点,但还没有完全意识到这对我试图创建的序列意味着什么。

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

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

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

我尚未修复代码以使其正常工作,但希望让其他人知道,以防他们遇到类似问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-20
    • 1970-01-01
    • 2023-02-14
    • 1970-01-01
    • 1970-01-01
    • 2017-06-30
    • 1970-01-01
    • 2020-08-08
    相关资源
    最近更新 更多