Выполнить код в дочерних элементах React после родительского componentDidMount

Я использую axios для веб-запросов и создал для него перехватчик, чтобы показывать тостер для всех сообщений об ошибках.

Я использую 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, получивший ответ об ошибке, вызовет всплывающее сообщение.

Моя проблема заключается в следующем. Если я попытаюсь вызвать с помощью axios до того, как будет вызван Main.componentDidMount, сообщение не появится.

Если я сделаю вызов в компоненте-потомке 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);

Одним из обходных путей может быть использование конструктора (или componentWillMoount) объекта Main для регистрации перехватчика, но это не подходит для асинхронного рендеринга, поскольку не гарантируется, что эти методы будут выполняться только один раз.

Могу ли я как-то изменить порядок вызовов 2 componentDidMount или использовать для этого какие-либо другие методы жизненного цикла?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
0
911
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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, но это не гарантирует, что он запустится только один раз, и потенциально может регистрировать один и тот же перехватчик много раз.

vinczemarton 25.03.2019 14:12

Тогда вы должны использовать только первую часть решения и return null или какой-нибудь индикатор занятости вместо дочерних элементов на первом рендере.

UjinT34 25.03.2019 14:29

Другие вопросы по теме