#reactjs #react-redux #react-hooks #infinite-loop #dispatch
Вопрос:
Я пытаюсь разработать приложение React, которое показывает список элементов с помощью крючков, но происходит бесконечный цикл. Я думаю, что состояние не идентифицируется как обновленное, но я не могу сказать, почему.
Я видел в документации, что это могут быть зависимости, которые я должен добавить в функцию useEffect, но когда я удаляю зависимости от useEffect, это решает проблему. Более того, это не то, что рекомендует делать документация, они рекомендуют поместить все зависимости в список.
Кодовое поле находится здесь: https://codesandbox.io/s/react-listing-forked-6sd99 и если я раскомментирую строку 30, Dashboard.ts
то можно увидеть бесконечный цикл, это строка, которая вызывает dispatch
функцию (а также codesandbox замерзнет на несколько секунд).
Функции useSelector и useEffect, как показано ниже, расположены в Dashboard.tsx
файле:
const catalog = useSelector<RootState, MetricState>(
(state) => state.fetchMetrics
);
console.log(catalog);
const dispatch = useDispatch();
const classes = useStyles();
useEffect(() => {
// dispatch(appReducer.actionCreators.fetchMetrics());
},[catalog, dispatch]));
Издевательские данные, как показано ниже, в apiCreators.js
файле
export function fetchMetrics() {
const action = {
type: ACTION_TYPES.FETCH_METRICS,
payload: []
};
return fetchMetricsCall(action);
const fetchMetricsCall = (action) => async (dispatch) => {
try {
dispatch({
type: action.type,
payload: {
metrics: [
{
name: "one",
owner: {
team: "test"
}
},
{
name: "one",
owner: {
team: "test"
}
}
] //contains the data to be passed to reducer
}.metrics
});
} catch (e) {
dispatch({
type: ACTION_TYPES.FAILURE,
payload: console.log(e) //TODO: fix return type
});
}
};
Комментарии:
1. Я не могу воспроизвести вашу ошибку, я раскомментирую строку, и ошибка не возникает
2. Извините, чего-то не хватало, обновляю код!
Ответ №1:
Главная проблема заключается catalog
в том, что внутри [catalog, dispatch]
можно найти здесь:
useEffect(() => {
// dispatch(appReducer.actionCreators.fetchMetrics());
},[catalog, dispatch]));
Проблема в том, что dispatch(appReducer.actionCreators.fetchMetrics())
это вызывает state.fetchMetrics (aka catalog)
обновление (которое является объектом), и javascript «странный», когда дело доходит до сравнения объектов/массивов.
Итак, давайте посмотрим на это:
let x = {a: 0}
let y = {a: 0}
let z = x
console.log(x === y) // this is FALSE
console.log(x === x) // this is TRUE
console.log(z === x) // this is TRUE
Эта статья лучше справляется с этим: http://adripofjavascript.com/blog/drips/object-equality-in-javascript.html
useEffect
делает что-то вроде if(oldState.catalog !== newState.catalog) {...rerender component}
всякий раз, когда происходит изменение состояния (я чувствую, что это может быть действительно неточно, но я надеюсь, что это поможет проиллюстрировать, что происходит). Таким образом , компонент повторно отправляет и выполняет новый вызов dispatch
, который обновляет состояние, и поскольку одна из зависимостей внутри useEffect
является объектом, невозможно выполнить обновление, которое сообщит useEffect
«прекратить повторную передачу».
Есть способы обойти это, но вам не должна понадобиться catalog
внутренняя [catalog, dispatch]
часть . Простое удаление, по-видимому, устраняет проблему?
Комментарии:
1. да, удаление каталога решило проблему, но мне было интересно узнать, почему, теперь я понял! Спасибо вам за ваши пояснения и ссылки! 🙂