React生命周期之componentDidMount详解

7,431 阅读1分钟

组件数据初始化

一般为了提前 setState ,防止二次渲染(第一次是空state渲染,第二次外部数据渲染),经常在 componentWillMount 生命周期请求数据。

// Before
class ExampleComponent extends React.Component {
  state = {
    externalData: null,
  };

  componentWillMount() {
    this._asyncRequest = asyncLoadData().then(
      externalData => {
        this._asyncRequest = null;
        this.setState({externalData});
      }
    );
  }

  componentWillUnmount() {
    if (this._asyncRequest) {
      this._asyncRequest.cancel();
    }
  }

  render() {
    if (this.state.externalData === null) {
      // Render loading state ...
    } else {
      // Render real UI ...
    }
  }
}

但是事实却不是这样的,异步获取外部数据不一定会在渲染之前返回,这也意味着组件也有可能会被渲染一次,为了后面新版本实现异步渲染,建议请求放在 componentDidMount 来调用。

还有一个问题是,componentWillMount 在服务端渲染(nuxt.js)的时候会导致服务端和客户端各渲染一次,而 componentDidMount 只在客户端渲染一次。

// After
class ExampleComponent extends React.Component {
  state = {
    externalData: null,
  };

  componentDidMount() {
    this._asyncRequest = loadMyAsyncData().then(
      externalData => {
        this._asyncRequest = null;
        this.setState({externalData});
      }
    );
  }

  componentWillUnmount() {
    if (this._asyncRequest) {
      this._asyncRequest.cancel();
    }
  }

  render() {
    if (this.state.externalData === null) {
      // Render loading state ...
    } else {
      // Render real UI ...
    }
  }
}

事件监听和解绑

事件监听最好在 componentDidMount 来实现,因为只有在调用 componentDidMount 的时候,React才会确保 componentWillUnmount 回调能顺利执行,防止内存泄漏。

class ExampleComponent extends React.Component {
  state = {
    subscribedValue: this.props.dataSource.value,
  };

  componentDidMount() {
    // Event listeners are only safe to add after mount,
    // So they won't leak if mount is interrupted or errors.
    this.props.dataSource.subscribe(
      this.handleSubscriptionChange
    );

    // External values could change between render and mount,
    // In some cases it may be important to handle this case.
    if (
      this.state.subscribedValue !==
      this.props.dataSource.value
    ) {
      this.setState({
        subscribedValue: this.props.dataSource.value,
      });
    }
  }

  componentWillUnmount() {
    this.props.dataSource.unsubscribe(
      this.handleSubscriptionChange
    );
  }

  handleSubscriptionChange = dataSource => {
    this.setState({
      subscribedValue: dataSource.value,
    });
  };
}