在父 componentDidMount 之后在 React 子级中执行代码

Execute code in React children after parent componentDidMount

我将 axios 用于 Web 请求,并为其创建了一个拦截器以显示所有错误消息的烤面包机。

我正在使用 react-intl 进行翻译,并且拦截器中存在的一般错误消息已被翻译,因此我将拦截器绑定到我的应用程序的生命周期中:

class Main extends React.Component {
  componentDidMount () {

    // addToastInterceptor calls back for a message that can be evaluated dynamically
    // otherwise it uses axios.interceptors.response.use(...)
    this.interceptor = addToastInterceptor((e) =>
      this.props.intl.formatMessage({
        id: 'applicationForm.basic.errorMessage'
      }, {
        technicalMessage: e.message
      }));
  }

  componentWillUnmount () {
    // the interceptor handle is removed when the component unmounts
    removeToastInterceptor(this.interceptor);
  }

  render() {
    // any number of child component in any depth
  }
}

// The intl provider must exist in a wrapper component
export default injectIntl(Main);

这样,当挂载 Main 组件时,任何接收到错误响应的 axios 调用都会触发 toast 消息。

我的问题如下。如果我在调用 Main.componentDidMount 之前尝试使用 axios 进行调用,则消息将不会显示。

如果我在后代组件的 componentDidMount 中进行调用,它不会显示消息:

// This component dispatches a redux call that uses axios.get internally
class SomeChild extends React.Component {
  componentDidMount () {
    // this is 
    this.props.getCountriesAction();
  }
}

const mapStateToProps = state => ({
  countries: state.boarding.countries,
});

const mapDispatchToProps = dispatch => bindActionCreators({
  getCountriesAction: getCountries,
}, dispatch);

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(SomeChild);

一种解决方法是使用 Main 的构造函数(或 componentWillMoount)来注册拦截器,但这不适用于异步渲染,因为这些方法不能保证 运行 只有一次。

我能否以某种方式更改 2 componentDidMount 调用的顺序或为此使用任何其他生命周期方法?

我不确定 addToastInterceptor 是做什么的。我觉得在constructor里面调用就好了。 如果确实需要在 children 的生命周期方法之前在 componentDidMount 中完成某些工作,您可以使用标志延迟 children 渲染,直到一切准备就绪:

class Main extends React.Component {
  state = {isReady: false}

  componentDidMount () {

    // addToastInterceptor calls back for a message that can be evaluated dynamically
    // otherwise it uses axios.interceptors.response.use(...)
    this.interceptor = addToastInterceptor((e) =>
      this.props.intl.formatMessage({
        id: 'applicationForm.basic.errorMessage'
      }, {
        technicalMessage: e.message
      }));

    this.setState({isReady: true});
  }

  render {
    if (!this.state.isReady) return null;
    ...
  }
}

如果 componentDidMount 中的工作需要很长时间,并且您想渲染一些东西,您可以将 isReady 标志传递给 children 并将它们的逻辑从 componentDidMount 移走到 componentDidUpdate:

componentDidUpdate(prevProps) {
  if (this.props.isReady && this.props.isReady !== prevProps.isReady) {
    /* child's logic from componentDidMount */
  }
}