Реакция использования Reducer Hook срабатывает дважды / как передать реквизит редуктору? - PullRequest
4 голосов
/ 08 марта 2019

ПРЕДИСЛОВИЕ / ОПИСАНИЕ

Я пытаюсь использовать новую функцию React для веб-сайта электронной коммерции, который я создаю, и у меня возникла проблема, связанная с ошибкой в ​​компоненте моей корзины для покупок.

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

ПРОБЛЕМА

Проблема, с которой я столкнулся, заключается в том, что когда я отправляю действие по добавлению компонента в корзину, редуктор запускается дважды, как если бы я дважды добавлял элемент в корзину. Но только когда он изначально отображается или по странным причинам, таким как отображение установлено на hidden, а затем обратно на block или для изменения z-index и, возможно, других подобных изменений.

Я знаю, что это довольно многословно, но это довольно вязкая и требовательная проблема, поэтому я создал два кодекса, демонстрирующих эту проблему:

полный пример

минимальный пример

Вы увидите, что я включил кнопку для переключения display компонентов. Это поможет продемонстрировать корреляцию css с проблемой.

Наконец, проследите за консолью в ручках кода, там будут показаны все нажатия кнопок и какая часть каждого редуктора была запущена. Проблемы наиболее очевидны в полном примере , но операторы консоли отображают проблему также в минимальном примере 1034 *.

ОБЛАСТЬ ПРОБЛЕМЫ

Я точно определил, что проблема связана с тем, что я использую состояние useContext для получения списка элементов. Вызывается функция для генерации редуктора для моего хука useReducer, но она возникает только тогда, когда используется другой хук. Я могу использовать функцию, которая не подлежит повторному вычислению, как хук, и не иметь проблемы, но Мне также нужна информация из моего предыдущего контекста, чтобы обходной путь не помог мне решить проблему.

Соответствующие ссылки

Я определил, что проблема не в HTML, поэтому я не буду включать ссылки на исправления, которые я пробовал. Проблема, вызванная css, не связана с css, поэтому я не буду также включать ссылки css.

useReducer Действие отправлено дважды

1 Ответ

11 голосов
/ 08 марта 2019

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

Если вы не можете просто переместить редуктор из функционального компонента из-за зависимостей от реквизита, контекста или другого состояния, решение состоит в том, чтобы запоминать ваш редуктор с помощью useCallback, чтобы вы могли только создайте новый редуктор при изменении его зависимостей (например, productsList в вашем случае).

Еще одна вещь, которую нужно иметь в виду, это то, что вам не нужно слишком беспокоиться о том, что ваш редуктор будет выполнен дважды за одну отправку. Предположение, которое делает React, заключается в том, что редукторы, как правило, будут достаточно быстрыми (они не могут ничего сделать с побочными эффектами, совершают вызовы API и т. Д.), И что риск повторного их выполнения в определенных сценариях стоит того. чтобы избежать ненужных повторных визуализаций (которые могут быть намного дороже, чем редуктор, если под элементом с редуктором имеется большая иерархия элементов).

Вот модифицированная версия Provider с использованием useCallback:

const Context = React.createContext();
const Provider = props => {
  const memoizedReducer = React.useCallback(createReducer(productsList), [productsList])
  const [state, dispatch] = React.useReducer(memoizedReducer, []);

  return (
    <Context.Provider value={{ state, dispatch }}>
      {props.children}
    </Context.Provider>
  );
}

Вот модифицированная версия вашего кода: https://codepen.io/anon/pen/xBdVMp?editors=0011

Вот пара ответов, связанных с useCallback, которые могут быть полезны, если вы не знакомы с тем, как использовать этот хук:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...