Как я могу вызвать `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
};
Этот поток позволит избежать любых петель или условий гонки.