Почему зависимость функции в хуках не вызывает бесконечных рендеров - PullRequest
2 голосов
/ 15 июня 2019

Ниже приведен фрагмент ссылки на codeandbox :

// function getFetchUrl(query) {
//   return "https://hn.algolia.com/api/v1/search?query=" + query;
// }
function App() {
  const [reactResult, setReactResult] = useState(null);
  const [reduxResult, setReduxResult] = useState(null);
  function SearchResults() {
    // ? Re-triggers all effects on every render
    // const getFetchUrl = useCallback((query) =>  {
      // return "https://hn.algolia.com/api/v1/search?query=" + query;
    // }, []);


     function getFetchUrl(query) {
       return "https://hn.algolia.com/api/v1/search?query=" + query;
     }

    useEffect(() => {
      console.log("running effect: 15");
      setReactResult(getFetchUrl("react"));
      // ... Fetch data and do something ...
      // }, [getFetchUrl]); // ? Deps are correct but they change too often
    }, [getFetchUrl]);

    useEffect(() => {
      console.log("running effect: 21");
      setReduxResult(getFetchUrl("redux"));
      // ... Fetch data and do something ...
      // }, [getFetchUrl]); // ? Deps are correct but they change too often
    }, [getFetchUrl]);

    // ...
  }

  SearchResults();

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>{reactResult}</h2>
      <h2>{reduxResult}</h2>
    </div>
  );
}

Вывод в консоли:

running effect: 15
running effect: 21
running effect: 15
running effect: 21

Я проверил this ответ, и я получаю, что функции переопределяются, что приводит к повторному запуску useEffect (во второй раз).Но я хочу прояснить одно сомнение:

Когда useEffect запускается во второй раз, он вызывает функции stateSetter (что требует от React рендеринга компонента снова).

Так не должен ли приведенный выше фрагмент работать в бесконечном цикле?

Пример и базовое понимание взяты из Полное руководство по использованию эффекта

Ответы [ 2 ]

3 голосов
/ 16 июня 2019

При использовании useState реакция достаточно умна, чтобы пропустить повторный рендеринг, если значение состояния фактически не изменилось, несмотря на вызов функции setState.(Это задокументировано в https://reactjs.org/docs/hooks-reference.html#bailing-out-of-a-state-update)

Пример кода, который вы включили, немного отличается от связанного кодового пера и на самом деле приведет только к одному «набору» сообщений журнала консоли.

running effect: 15
running effect: 21

последовательность событий:

  1. Первоначальный рендеринг, запускает оба эффекта и обновляет оба состояния reactResult и reduxResult. Это ставит в очередь повторный рендеринг.
  2. Компонент выполняет рендеринг. В вашем включенном примере вы используете useCallback без зависимостей, которые будут возвращать предыдущее значение, и, следовательно, эффекты не будут выполняться.

С другой стороны,в вашем кодовом пере вместо useCallback вы переопределяете обратный вызов при каждом выполнении, в этом случае вы получите два «набора» консольного сообщения:

  1. Первоначальный рендеринг, запускает оба эффекта и обновляет обасостояние reactResult и reduxResult. Это приводит к повторной визуализации.
  2. Компонент выполняет повторную визуализацию. getFetchUrl является локальной функцией, поэтому не равно getFetchUrl с предыдущего запуска.Sult эффекты будут повторяться.Однако setReactResult и setReduxResult вызываются с тем же значением, что и раньше, поэтому повторный рендеринг будет не сработать.
0 голосов
/ 16 июня 2019

Ваши эффекты срабатывают только при изменении getFetchUrl ... и, поскольку это запомненный обратный вызов (который не изменяется), эффекты запускаются только один раз.

...