Задержка вызова useEffect вызывает несоответствия модели компонента - PullRequest
0 голосов
/ 05 ноября 2019

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

Вот простой пример, где useEffect используется для запуска вычисления resultValue на основе значения inputValue:

function App() {
  const [inputValue, incrementInputValue] = useReducer((s, _) => s + 1, 0);
  const [resultValue, setResultValue] = useState(0);

  useEffect(() => {
    // It could be an asynchronous call to business logic etc.
    setResultValue(inputValue * 2);
  }, [inputValue]);

  console.log("Render: %d * 2 = %d", inputValue, resultValue);

  return (
    <div className="App">
      <p>{inputValue} * 2 = {resultValue}</p>
      <button onClick={() => { console.log('Click'); incrementInputValue(undefined); }}>
        Increment
      </button>
    </div>
  );
}

Этот код возвращает следующий журнал:

Render: 0 * 2 = 0
Click 
Render: 1 * 2 = 0 // inputValue was updated, but useEffect not called yet
Render: 1 * 2 = 2
Click 
Render: 2 * 2 = 2 // inputValue was updated, but useEffect not called yet
Render: 2 * 2 = 4

Код Песочница

Каков наилучший подход, чтобы избежать таких несоответствий, вызванных отложенным вызовомиспользованияEffect? ​​

1 Ответ

1 голос
/ 05 ноября 2019

Существует целый жизненный цикл, который запускается после отправки действия (incrementInputValue), вы не сделаете его согласованным без "удаления" этого жизненного цикла:

  1. Нажатие кнопки.
  2. Отправка действия (incrementInputValue)
  3. Повторная визуализация дуэта с изменением состояния (inputValue)
  4. Запуск useEffect обратного вызова дуэта до inputValue изменения.
  5. Повторная визуализация дуэта с изменением состояния (resultValue)

Поэтому, как уже говорилось, вам нужно избавиться от фазы 3.

Есть некоторыеРешения, вы можете использовать ссылку или объединить ее в одном состоянии.

const reducer = (state, increase) => ({
  inputValue: state.inputValue + increase,
  resultValue: state.resultValue + increase * 2
});

const initial = { inputValue: 0, resultValue: 0 };

function App() {
  const [state, increaseResult] = useReducer(reducer, initial);

  useEffect(() => {
    console.log(state);
  });

  return (
    <div className="App">
      <p>
        {state.inputValue} * 2 = {state.resultValue}
      </p>
      <button
        onClick={() => {
          console.log('Click');
          increaseResult(1);
        }}
      >
        Increment
      </button>
    </div>
  );
}

Каков наилучший подход? Это зависит от варианта использования, в большей степени вопрос о том, когда и почему использовать ссылку вместо состояния.

В этом конкретном примере можно утверждать, что вы можете вывести всю логику одним делением: inputValue = resultValue / 2.

Edit gifted-poitras-3bng4

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