Пропустить сначала useEffect Render Custom Hook - PullRequest
1 голос
/ 02 ноября 2019

Я хочу создать пользовательский хук, который пропустит первый рендеринг useEffect и использует его везде.

Я создал рабочий пример пользовательского хука:

function useEffectSkipFirst(fn, arr) {
  const isFirstRun = useRef(true);

  useEffect(() => {
    if (isFirstRun.current) {
      isFirstRun.current = false;
      return;
    }

    fn();
  }, [...arr]);
}

Как использовать:

useEffectSkipFirst(fn, []);

Проблема в том, что я получил несколько предупреждений, которые я пытаюсь понять, и надеюсь, что вы мне поможете:

У React Hook useEffect отсутствует зависимость: 'fn'. Либо включите его, либо удалите массив зависимостей. Если 'fn' меняется слишком часто, найдите родительский компонент, который его определяет, и перенесите это определение в useCallback. (Reaction-hooks / исчерпывающее-deps) eslint

React Hook useEffect имеет элемент расширения в своем массиве зависимостей. Это означает, что мы не можем статически проверить, правильно ли вы передали правильные зависимости. (ответные зацепки / исчерпывающие действия)

Пример кодов и ящиков.

Полный пример кода:

import React, { useRef, useEffect, useState } from "react";
import ReactDOM from "react-dom";

function useEffectSkipFirst(fn, arr) {
  const isFirstRun = useRef(true);

  useEffect(() => {
    if (isFirstRun.current) {
      isFirstRun.current = false;
      return;
    }

    fn();
  }, [...arr]);
}

function App() {
  const [clicks, setClicks] = useState(0);
  const [date, setDate] = useState(Date.now());
  const [useEffectRunTimes, setTseEffectRunTimes] = useState(0);

  useEffectSkipFirst(() => {
    addEffectRunTimes();
  }, [clicks, date]);

  function addEffectRunTimes() {
    setTseEffectRunTimes(useEffectRunTimes + 1);
  }

  return (
    <div className="App">
      <div>clicks: {clicks}</div>
      <div>date: {new Date(date).toString()}</div>
      <div>useEffectRunTimes: {useEffectRunTimes}</div>
      <button onClick={() => setClicks(clicks + 1)}>add clicks</button>
      <button onClick={() => setDate(Date.now())}>update date</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Ответы [ 3 ]

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

Первое предупреждение в основном означает, что все зависимости, на которые ссылается обратный вызов useEffect, должны быть объявлены внутри его массива зависимостей. Официальная документация предоставляет дополнительные пояснения о добавлении функций в массив зависимостей.

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

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

Я вижу одно решение вашей проблемы.

Сначала необходимо уменьшить массив зависимостей useEffect до одного значения (с помощью ловушки useMemo).

Тогда в результате вы должны использовать useRef для хранения старого значения этого значения зависимости, чтобы вы могли вызывать функцию fn только тогда, когда зависимость изменилась .

Вот код пользовательского хука:

function useEffectSkipFirst(fn, depValue) {
  const oldDepValueRef = useRef(depValue);

  useEffect(() => {
    if (depValue !== oldDepValueRef.current) {
      // Note that this code will not be executed the first time as
      // we initialized oldDepValueRef with depValue
      fn();
      oldDepValueRef.current = depValue;
    }
  }, [fn, depValue]);
}

Вот код, чтобы уменьшить массив массива до одного значения, используя useMemo:

// I decided the return an object but it could be an array or something else
const depValue = useMemo(() => ({}), [clicks, date]);

useEffectSkipFirst(addEffectRunTimes, depValue);

function addEffectRunTimes() {
  // When the value depends on the previous value, 
  // you can use a function to update the state value
  setTseEffectRunTimes(v => v + 1);
}

Кстати, если в эффекте используется только функция addEffectRunTimes, вы можете написать:

useEffectSkipFirst(() => setTseEffectRunTimes(v => v + 1), depValue);
1 голос
/ 02 ноября 2019

Функция fn, которую вы берете в качестве обратного вызова для useEffectSkipFirst, изменяется при каждом рендере. Кроме того, поскольку эта функция вызывается в useEffectSkipFirst, линтер предлагает вам определить ее как зависимость.

Однако способ, которым вы реализуете его, сводится к коду, который реализует useEffectSkipFirst, чтобы позаботиться о том, чтобы все значения имели правильные замыкания, и вы можете пропустить определение его как зависимости от useEffect. Однако, если вы это сделаете, это может привести к бесконечному циклу.

Вы также можете проверить этот пост для более подробной информации: Как исправить предупреждение об отсутствующей зависимости при использовании useEffect React Hook?

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