React hook useRender вызывается дважды, если выполняется аварийное завершение работы и после этого устанавливается состояние

Я не уверен, что это ожидаемое поведение, но если вы откажетесь от отправки (https://reactjs.org/docs/hooks-reference.html#bailing-out-of-a-dispatch) при использовании хука useReducer действие выполняется дважды, если оно с последующим рендерингом. Позволь мне объяснить:

// bailing out to prevent re-rendering
const testReducer = (state, action) => {
  switch (action.type) {
    case "ADD":
      state.test += 1
      return state;
  }
};

const myComponent = () => {
  let [totalClicks, setClicks] = useState(0);
    const [state, setState] = useReducer(testReducer, {
      test: 0,
    });

  const clickHandler = () => {
    setState({type: 'ADD'});
    setClicks((totalClicks += 1));
  };

  return (
    <div>
      <button onClick={clickHandler}>+</button>
      <p>{totalClicks}</p>
      <p>test count: {state.test}</p>
    </div>
  );
}

Когда вы нажимаете кнопку, state.test увеличивается на 2, а totalClicks увеличивается на 1. Однако, если бы я изменил редуктор так, чтобы он не переходил к одному, как показано ниже, они оба увеличились бы на 1 .

// non-bailing reducer
const testReducer = (state, action) => {
  switch (action.type) {
    case "ADD":
      return {
        test: state.test + 1,
      };
  }
};

Почему это? Это ожидаемое поведение или ошибка? Пример песочницы: https://codesandbox.io/s/sad-robinson-dds63?file=/src/App.js.


ОБНОВЛЕНИЕ: после выполнения некоторой отладки похоже, что такое поведение происходит только при обертке с помощью React.StrictMode

Кто-нибудь знает, что вызывает это ???


person webbyweb    schedule 04.05.2020    source источник
comment
Можете ли вы создать для этого воспроизводимый пример. Я создал один и не обнаружил никаких проблем. codeandbox.io/s/hooks-state-non-pure-update- d83uo   -  person chandan_kr_jha    schedule 04.05.2020
comment
Прикрепил. Посмотрев на ваш, я не понимаю разницы в том, что я делал, и почему мой ведет себя так. codeandbox.io/s/sad-robinson-dds63?file = / src / App.js   -  person webbyweb    schedule 04.05.2020


Ответы (1)


Согласно doc StrictMode намеренно реагировать на вызовы reducer с одним и тем же действием дважды, чтобы выявить незамеченные потенциально опасные побочные эффекты, что и происходит в вашем случае.

Строгий режим не может автоматически обнаруживать побочные эффекты, но он может помочь вам обнаружить их, сделав их более детерминированными. Это делается путем намеренного двойного вызова следующих функций: […] Функции, переданные в useState, useMemo или useReducer

person hackape    schedule 04.05.2020
comment
Вау, это интересно. Так что насчет небезопасного примера? Дело в том, что мы обновляем состояние useState и состояние useReducer в одном и том же событии щелчка? - person webbyweb; 04.05.2020
comment
Речь идет о том, что вы нарушаете предположение, что редуктор должен быть свободным от побочных эффектов, идемпотентным и всегда возвращать новое значение при изменении. Вы нарушаете третье правило (хотя и намеренно). Для меня это больше похоже на конвенцию. Но поскольку существует множество экосистем и механизмов (например, спасение), построенных вокруг этого предположения, это становится обязательным требованием. Итак, strictMode решает раскрыть его. - person hackape; 04.05.2020