首页 > 解决方案 > React 组件生命周期和启动异步流程

问题描述

考虑一个 ReactJS 场景,一个有状态的组件负责从外部 HTTP API 获取信息并呈现它。它还负责重新获取该信息以响应 DOM 事件(例如按钮单击)或时间间隔。

如果我正确理解了新的 ReactJS 16.3 生命周期:

但是,我有一个关于响应异步事件的额外提取的问题,例如按钮单击处理程序或setInterval处理程序:

类似的情况是如果由于组件props更改而需要新的提取。在这种情况下,我假设componentDidUpdate始终是开始新获取的正确位置,即因为getDerivedStateFromPropsshouldComponentUpdate发生在渲染阶段

谢谢,佩德罗

标签: reactjs

解决方案


相信大家看一些示例代码会比较容易。如果我不使用 Redux,我会这样做:

import React, { Component } from "react";

class ItemView extends Component {
  state = {
    fetching: false
  };

  componentDidMount() {
    this.fetchItems();

    // Fetch items every 10 seconds
    this.interval = setInterval(() => {
      this.fetchItems();
    }, 10 * 1000);
  }

  componentWillUnmount() {
    clearInterval(this.interval);

    if (this.state.fetching) {
      this.isUnmounted = true;
    }
  }

  fetchItems = () => {
    this.setState({
      fetching: true
    });

    return fetch("https://api.example.org/items").then(response => {
      if (!this.isUnmounted) {
        this.setState({
          fetching: false,
          items: response.json().items
        })
      }
    }).catch(e => {
      if (!this.isUnmounted) {
        this.setState({
          fetching: false,
          error: e.message
        });
      }
    })
  };
}

现在,回答您的问题:

  1. getDerivedStateFromProps是一个同步操作,在初始渲染后和收到新道具后触发。因此,我不建议将异步操作放在这里。它也是渲染阶段的一部分(检查第二点)。

  2. React 生命周期有两个阶段:TherendercommitPhase。React 会比较阶段中要完成的更改,render并且此阶段的生命周期可以在发生之前多次触发commit。对 DOM 的更改是在commit阶段中进行的。ComponentDidUpdate在阶段之后调用commit,因此我建议它是执行异步操作的正确位置,以便您可以显示加载状态,直到您收到来自 API 的响应并且您不会多次触发请求。

  3. 最后但同样重要的是,我会使用 Redux 并将 fetch 逻辑移到组件之外,这样我们就不必处​​理竞争条件(例如,组件之类的情况已经卸载,直到我们收到来自 api 或父组件的响应同时更新道具)。

如果您不想使用该isUnmounted模式(请在此处查看原因),您可以考虑使用可取消的承诺。


推荐阅读