【问题标题】:Execute code in React children after parent componentDidMount在父组件DidMount之后在React子中执行代码
【发布时间】:2019-08-15 15:28:54
【问题描述】:

我将 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 调用的顺序或为此使用任何其他生命周期方法?

【问题讨论】:

    标签: reactjs axios lifecycle


    【解决方案1】:

    我不确定addToastInterceptor 做了什么。我认为可以在constructor 中调用它。 如果在儿童的生命周期方法之前确实需要在componentDidMount 内完成某些工作,您可以使用一个标志来延迟儿童渲染,直到一切准备就绪:

    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 标志传递给孩子并将他们的逻辑从componentDidMount 移动到componentDidUpdate

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

    【讨论】:

    • 我真的不希望传递显式参数,尤其是因为孩子不一定是直接孩子,而是后代。我可以使用上下文,但这会有点沉重。我很乐意使用 ctor,但不能保证只运行一次,它可能会多次注册同一个拦截器。
    • 那么你应该只使用解决方案的第一部分和return null 或一些繁忙的指示器,而不是在第一次渲染时使用孩子
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-16
    • 2018-07-30
    相关资源
    最近更新 更多