Как я могу вызвать `dispatch` изнутри`componentDidUpdate`, не попадая в бесконечный цикл?

Я делаю очень простой компонент React, который показывает данные, полученные из удаленного источника. Перед извлечением данных необходимо дождаться правильного входа пользователя. Вот как я пытаюсь это сделать:

class MyComponent extends React.Component {
  componentDidUpdate () {
    if (this.props.loggedIn) {
      api.getData()
        .then(() => {
          this.props.dispatch({
            type: 'gotData'
          })
        }, (reason) => {
          this.props.dispatch({
            type: 'getDataFailed'
          })
        })
    }
  }
}

По моим собственным словам, каждый раз, когда часть государства имеет отношение к этому компоненту (в данном случае, loggedIn prop) обновляется, componentDidUpdate называется, и я могу получить данные, если я вижу, что пользователь вошел в систему.

Это на самом деле работает очень хорошо, за исключением одной серьезной проблемы: кажется, что вызов dispatch в конечном итоге вызывает componentDidUpdate опять же, так что я в конечном итоге в бесконечном цикле. Мне даже не нужно нигде прислушиваться к этим событиям отправки, простой факт использования dispatch функции достаточно, чтобы вызвать componentDidUpdate снова.

Я предполагаю, что диспетчер выполняет мой корневой редуктор, который внутренне использует setStateтем самым вызывая целое update цепь жизненного цикла снова.

Как обычно это делается? Как я могу позвонить dispatch изнутри componentDidUpdate без попадания в бесконечный цикл?

2 ответа

Решение

Один из простых способов решить эту проблему - добавить дополнительный флаг, например loggingIn, которые вы установили (отправив) перед асинхронным действием и сбросили после. Вам также необходимо отслеживать, когда при входе в систему не удалось отличить эту ситуацию от того, когда процесс входа не был запущен (если только вы не хотите перезапустить процесс автоматически при сбое):

class MyComponent extends React.Component {
  componentDidUpdate () {
    const { loggedIn, loggingIn, loginFailed, dispatch } = this.props;
    if (!loggingIn && !loggedIn && ! loginFailed) {
      dispatch({
        type: 'gettingData' // this one sets loggingIn to true, loggedIn to false, loginFailed to false
      });
      api.getData()
        .then(() => {
          dispatch({
            type: 'gotData' // this one sets loggingIn to false, loggedIn to true, loginFailed to false
          })
        }, (reason) => {
          dispatch({
            type: 'getDataFailed' // this one sets loggingIn to false, loggedIn to false, loginFailed to true
          })
        })
    }
  }
}

Если вы не хотите устанавливать это состояние в избыточном, вы также можете иметь этот элемент управления в самом компоненте:

class MyComponent extends React.Component {
  componentDidUpdate () {
    const { loggedIn, dispatch } = this.props;
    const { loggingIn, loginFailed } = this.state;

    if (!loggingIn && !loggedIn && !loginFailed) {
      this.setState({
        loggingIn: true
      })
      api.getData()
        .then(() => {
          this.setState({
            loggingIn: false,
            loginFailed: false
          });
          dispatch({
            type: 'gotData'
          })
        }, (reason) => {
          this.setState({
            loggingIn: false,
            loginFailed: true
          });
          dispatch({
            type: 'getDataFailed'
          })
        })
    }
  }
}

Вы могли бы использовать loggingIn флаг, чтобы отобразить счетчик для вашего пользователя, или loginFailed флаг для отображения сообщения, если оно соответствует вашему UX.

Переместите свой вызов API в действия, когда вы получили статус авторизованного! Что-то вроде этого:

api.getUserLoggedInStatus().then((status) => {
  dispatch({
    type: 'loggedInStatus',
    status
  });
  api.getData.... // and dispatch
};

Этот поток позволит избежать любых петель или условий гонки.

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