Как исправить отсутствующую зависимость в React Hook useEffect

С React 16.8.6 (предыдущая версия была хороша для 16.8.3) я получаю эту ошибку, когда пытаюсь предотвратить бесконечный цикл при запросе выборки

./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

Я не смог найти решение, которое бы хорошо остановило бесконечный цикл. Я хочу держаться подальше от использования useReducer(), Я нашел это обсуждение https://github.com/facebook/react/issues/14920 где возможное решение You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing. Я не уверен в том, что я делаю, поэтому я еще не пытался реализовать это.

У меня есть эта текущая настройка React ловушка useEffect работает постоянно навсегда / бесконечный цикл, и единственный комментарий о useCallback() с которым я не знаком.

Как я сейчас использую useEffect() (который я хочу запустить только один раз в начале componentDidMount())

useEffect(() => {
    fetchBusinesses();
  }, []);
const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

27 ответов

Решение

Если вы не используете метод fetchBususiness где-либо кроме эффекта, вы можете просто переместить его в эффект и избежать предупреждения

useEffect(() => {
    const fetchBusinesses = () => {
       return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
  fetchBusinesses();
}, []);

Однако, если вы используете fetchBususiness за пределами рендера, вы должны отметить две вещи

  1. Если есть какие-либо проблемы, если вы не проходите fetchBusinesses как метод и то, что он используется во время монтирования с закрывающим замком, проблем не будет.
  2. Зависит ли ваш метод от каких-то переменных, которые он получает из закрытого замыкания, что не так для вас.
  3. При каждом рендеринге fetchBususiness будет воссоздан, и, следовательно, передача его в useEffect вызовет проблемы. Итак, сначала вы должны запомнить fetchBususiness, если вы должны были передать его в массив зависимостей.

Подводя итог, я бы сказал, что если вы используете fetchBusinesses вне useEffect Вы можете отключить правило, используя // eslint-disable-next-line react-hooks/exhaustive-deps в противном случае вы можете переместить метод внутри useEffect.

Вы можете установить его прямо как useEffect Перезвоните:

useEffect(fetchBusinesses, [])

Он сработает только один раз, поэтому убедитесь, что все зависимости функции установлены правильно (так же, как при использовании componentDidMount/componentWillMount...)


Изменить 21.02.2020

Просто для полноты:

1. Используйте функцию как useEffect обратный вызов (как указано выше)

useEffect(fetchBusinesses, [])

2. Объявите функцию внутри useEffect()

useEffect(() => {
  function fetchBusinesses() {
    ...
  }
  fetchBusinesses()
}, [])

3. Запомните с useCallback()

В этом случае, если у вас есть зависимости в вашей функции, вам нужно будет включить их в useCallback массив зависимостей, и это вызовет useEffectснова, если параметры функции изменятся. Кроме того, это много шаблонов... Так что просто передайте функцию прямо вuseEffect как в 1. useEffect(fetchBusinesses, []).

const fetchBusinesses = useCallback(() => {
  ...
}, [])
useEffect(() => {
  fetchBusinesses()
}, [fetchBusinesses])

4. Отключить предупреждение eslint

useEffect(() => {
  fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps
./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

Это не ошибка JS/React, а предупреждение Elsint.

Это говорит вам, что крючок зависит от функции fetchBusinesses, так что вы должны передать это как зависимость.

useEffect(() => {
  fetchBusinesses();
}, [fetchBusinesses]);

Это ничего не изменит, если fetchBusinessess вне компонента в работе, но предупреждение исчезнет.

Подробнее: https://github.com/facebook/react/issues/14920

Эти предупреждения очень полезны для поиска компонентов, которые не обновляются последовательно: https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of- зависимости.

Однако, если вы хотите удалить предупреждения по всему проекту, вы можете добавить это в свою конфигурацию eslint:

  {
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/exhaustive-deps": 0
    }
  }

Решение также дает реакция, они советуют вам использовать useCallback который вернет мемоизируемую версию вашей функции:

Функция 'fetchBus Business' изменяет зависимости хука useEffect (в строке NN) при каждом рендеринге. Чтобы исправить это, оберните определение fetchBuscies в его собственное useCallback() Hook response-hooks / excustive-deps

useCallback прост в использовании, так как имеет ту же подпись, что и useEffectразница в том, что useCallback возвращает функцию. Это выглядело бы так:

 const fetchBusinesses = useCallback( () => {
        return fetch("theURL", {method: "GET"}
    )
    .then(() => { /* some stuff */ })
    .catch(() => { /* some error handling */ })
  }, [/* deps */])
  // We have a first effect thant uses fetchBusinesses
  useEffect(() => {
    // do things and then fetchBusinesses
    fetchBusinesses(); 
  }, [fetchBusinesses]);
   // We can have many effect thant uses fetchBusinesses
  useEffect(() => {
    // do other things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);
const [mount, setMount] = useState(false)
const fetchBusinesses = () => { 
   //function defination
}
useEffect(() => {
   if(!mount) {
      setMount(true);
      fetchBusinesses();
   }
},[fetchBusinesses]);

Это довольно простое решение, и вам не нужно переопределять предупреждения es-lint. Просто установите флаг, чтобы проверить, смонтирован ли компонент или нет.

Просто отключите eslint для следующей строки;

useEffect(() => {
   fetchBusinesses();
// eslint-disable-next-line
}, []);

таким образом вы используете его так же, как компонент примонтировал (вызывается один раз)

обновлено

или

const fetchBusinesses = useCallback(() => {
 // your logic in here
 }, [someDeps])

useEffect(() => {
   fetchBusinesses();
// no need to skip eslint warning
}, [fetchBusinesses]); 

fetchBusshops будет вызываться каждый раз, когда что-то изменится

Эта статья является хорошим учебником по извлечению данных с помощью хуков: https://www.robinwieruch.de/react-hooks-fetch-data/

По сути, включить определение функции выборки внутри useEffect:

useEffect(() => {
  const fetchBusinesses = () => {
    return fetch("theUrl"...
      // ...your fetch implementation
    );
  }

  fetchBusinesses();
}, []);

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

Если вы не хотите ставить fetchBusinesses внутри зависимостей ловушки вы можете просто передать ее в качестве аргумента функции обратного вызова ловушки и установить основной fetchBusinesses в качестве значения по умолчанию для него вот так

useEffect((fetchBusinesses = fetchBusinesses) => {
   fetchBusinesses();
}, []);

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

Также, как написал Шубнам, вы можете добавить нижеприведенный код, чтобы ESLint игнорировал проверку вашего хука.

// eslint-disable-next-line react-hooks/exhaustive-deps

Ты попробуй этот путь

const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

а также

useEffect(() => {
    fetchBusinesses();
  });

это работа для вас. Но я предлагаю попробовать, этот способ тоже сработает для вас. Лучше, чем раньше. Я использую такой способ:

useEffect(() => {
        const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
        fetchBusinesses();
      }, []);

если вы получаете данные на основе определенного идентификатора, добавьте в обратный вызов useEffect [id] тогда не могу показать вам предупреждение React Hook useEffect has a missing dependency: 'any thing'. Either include it or remove the dependency array

Это не ответ для конкретного варианта использования вопроса, а более общий случай, и он охватывает случай, когда useEffect или extract and import не работают. Параметр useRef senario:

Иногда сценарий таков, что useEffect должен иметь пустой массив, и вы все еще хотите использовать внутри useEffect части состояния, но все же вы не хотите вводить их как зависимости, также вы можете попробовать useCallback, и теперь реагируете, жалуется на зависимости использования useCallback, и вы застряли. В этом случае в некоторых случаях можно использовать useRef. Например:

      const locationRef = useRef(location);
useEffect(()=>{
const qs = locationRef.current.search
...
},[])

Вы должны быть осторожны при использовании этой техники и знать, что useRef не активирует процесс рендеринга.

Well if you want to look into this differently, you just need to know what are options does the React has that non exhaustive-deps? One of the reason you should not use a closure function inside the effect is on every render, it will be re-created/destroy again.

So there are multiple React methods in hooks that is considered stable and non-exhausted where you do not have to apply to the useEffect dependencies, and in turn will not break the rules engagement of react-hooks/exhaustive-deps. For example the second return variable of useReducer or useState which is a function.

      const [,dispatch] = useReducer(reducer, {});

useEffect(() => {
    dispatch(); // non-exhausted, eslint won't nag about this
}, []);

So in turn you can have all your external dependencies together with your current dependencies coexist together within your reducer function.

      const [,dispatch] = useReducer((current, update) => {
    const { foobar } = update;
    // logic

    return { ...current, ...update };
}), {});

const [foobar, setFoobar] = useState(false);

useEffect(() => {
    dispatch({ foobar }); // non-exhausted `dispatch` function
}, [foobar]);

В моем случае это предупреждение было с моей локальной переменной, и когда я поставил organizationв массиве зависимостей будет извлекаться бесконечно. Поэтому, если у вас есть некоторые пробелы, подобные моему, используйте с массивом зависимостей и разделите:
потому что, если у вас есть несколько вызовов api, которые изменяют состояние, он вызывает useEffectнесколько раз
От:

        const { organization } = useSelector(withOrganization)
  const dispatch = useDispatch()

  useEffect(() => {
    dispatch(getOrganization({}))
    dispatch(getSettings({}))
    dispatch(getMembers({}))
  }, [dispatch, organization])

К:

        const { organization } = useSelector(withOrganization)
  const dispatch = useDispatch()

  useEffect(() => {
    dispatch(getOrganization({}))
    dispatch(getSettings({}))
  }, [dispatch, organization])

  useEffect(() => {
    dispatch(getMembers({}))
  }, [dispatch])

Вы можете удалить массив типа 2-го аргумента [] но fetchBusinesses()также будет вызываться каждое обновление. Вы можете добавитьIF заявление в fetchBusinesses() реализация, если хотите.

React.useEffect(() => {
  fetchBusinesses();
});

Другой - реализовать fetchBusinesses()функционировать вне вашего компонента. Только не забудьте передать аргументы зависимости вашемуfetchBusinesses(dependency) звоните, если есть.

function fetchBusinesses (fetch) {
  return fetch("theURL", { method: "GET" })
    .then(res => normalizeResponseErrors(res))
    .then(res => res.json())
    .then(rcvdBusinesses => {
      // some stuff
    })
    .catch(err => {
      // some error handling
    });
}

function YourComponent (props) {
  const { fetch } = props;

  React.useEffect(() => {
    fetchBusinesses(fetch);
  }, [fetch]);

  // ...
}

Просто передайте функцию в качестве аргумента в массиве useEffect...

useEffect(() => {
   functionName()
}, [functionName])

Если вы хотите отключить это бесполезное сообщение, просто добавьте его в начало вашего файла.

      /* eslint-disable react-hooks/exhaustive-deps */

Это отключит предупреждение для всего файла.

Найдите ключевые слова, чтобы узнать больше о каждом предупреждении. Чтобы игнорировать, добавьте // eslint-disable-next-line к предыдущей строке.

Например: функция, используемая в useEffect, вызывает предупреждение.

Вы можете избавиться от этого предупреждения Es-lint, передав ссылку на него:

Пример, упомянутый ниже, однако вы можете посмотреть решение по этой ссылке: https://www.youtube.com/watch?v=r4A46oBIwZk&t=8s

Предупреждение: Строка 13: 8: React Hook React.useEffect имеет недостающие зависимости: 'history' и 'currentUser? .Role'. Либо включите их, либо удалите массив зависимостей react-hooks / excustive-deps

      React.useEffect(() => {
    if (currentUser?.role !== "Student") {
        return history.push("/")
    }
}, [])

Решение. Шаг 1. Переместите бизнес-логику в отдельную константу.

Теперь предупреждение: React Hook React.useEffect имеет недостающую зависимость: 'roleChecking'.

      const roleChecking = () =>{
   if (currentUser?.role !== "Student") {
        return history.push("/")
    }
}

React.useEffect(() => {
    roleChecking()
}, [])

Последний шаг - создать ссылку на функцию:

        const roleRef = React.useRef();

  const roleChecking = () => {
    if (currentUser?.role !== "Student") {
      return history.push("/");
    }
  };
  roleRef.current = roleChecking;

  React.useEffect(() => {
   return roleRef.current();
  }, [currentUser?.role]);

Это предупреждение возникает, если переменные, которые вы используете внутри, определены внутри компонента или переданы компоненту как опора. Поскольку вы определили внутри одного и того же компонента, вы должны передать его в массив зависимостей.

Но если вы импортировали fetchBusinesses()а затем использовали его внутри, вам не нужно было бы добавлять его в массив зависимостей. Вот как мы на самом деле настраиваем наши приложения redux: мы всегда импортируем наших создателей действий и запускаем их внутри useEffect без добавления его в массив зависимостей.

То же верно и для useMemo тоже.

It seems the function declared in the component.It means in every render it declares new function which triggers the hook.

There are 2 approaches to fix the issue.

  1. Move the function declaration out of component.

  2. Wrap the fetchBusinesses function with useCallback hook.

First option is preferable.

используя UseEffect fetchBusinessesвызывающая функция объявляет в useEffect()объявив константную переменную после вызова имени функции,

      useEffect(()=>{
const fetchBusinesses=()=>{
   console.log(useeffect fetchbussinesses functions)
}
 fetchBusinesses();
},[declare the variable used in useeffect hooks ])

Чтобы отключить это предупреждение в вашем проекте

Добавь это"react-hooks/exhaustive-deps": "off"к.eslintrc.jsфайл

Я хочу только бежать [fetchBusinesses] один раз вначале похож наcomponentDidMount()

Вы можете тянуть fetchBusinesses полностью вне вашего компонента:

const fetchBusinesses = () => { // or pass some additional input from component as args
  return fetch("theURL", { method: "GET" }).then(n => process(n));
};

const Comp = () => {
  React.useEffect(() => {
    fetchBusinesses().then(someVal => {
      // ... do something with someVal
    });
  }, []); // eslint warning solved!
  return <div>{state}</div>;
};

Это не только предоставит простое решение, но и устранит проблему с исчерпывающими предупреждениями. fetchBusiness теперь можно тестировать лучше и упрощает Comp, поскольку он находится в области видимости модуля за пределами дерева React.

Переезд fetchBusinessesснаружи работает здесь хорошо, так как в любом случае мы сможем читать только начальные свойства и состояние из компонента из-за устаревшей области закрытия ([] Деп в useEffect).

Как опустить зависимости функций

  • Функция перемещения внутри эффекта
  • Переместить функцию за пределы компонента - (мы используем эту)
  • Вызвать функцию во время рендеринга и позволить useEffect зависят от этого значения (чистая вычислительная функция)
  • Добавьте функцию для эффекта deps и оберните ее useCallback в крайнем случае

По поводу других решений:

Тянуть fetchBusinesses внутри useEffect()на самом деле не помогает, если вы получаете доступ к другому состоянию в нем. eslint все равно будет жаловаться: Codesandbox.

Я бы также воздержался от исчерпывающих комментариев eslint-deps ignore comments. О них просто легко забыть, когда вы проводите некоторый рефакторинг и пересмотр своих зависимостей.

Попробуйте поместить функцию в useEffect. В моем случае это помогло. Удачного кодирования !!

добавить параметр зависимости загрузки внутри [] useffect

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

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

Таким образом, вам нужно будет добавить в [fetchBusinesses]и предупреждение должно исчезнуть.

Теперь, почему это правило хочет, чтобы мы поместили это туда?

Есть несколько сценариев, в которых использование и неправильное перечисление всех частей состояния и свойств внутри массива может привести к странным и трудным для отладки проблемам.

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

Теперь произвольное добавление к этому массиву также может привести к ошибкам. Так что в любом случае вы сталкиваетесь с ошибками, которые вы должны решить. Согласно вашим комментариям, которые, казалось, решили это для вас, но я хотел бы продолжить расследование, чтобы узнать, есть ли у вас случайно второй GETзапрос на вкладке « Сеть » в Chrome после добавления в fetchBusinessesфункция для вашего useEffectмножество.

Добавьте этот комментарий вверху файла, чтобы отключить предупреждение.

      /* eslint-disable react-hooks/exhaustive-deps */
Другие вопросы по тегам