Как правильно использовать useEffect для вызова asyn c fetch с реакцией? реагировать на крючки / исчерпывающие депс - PullRequest
2 голосов
/ 20 июня 2020

Привет, у меня проблема с хуком useEffect в React. Приведенный ниже код работает так, как должен, но es-lint предполагает, что мне нужно предоставить зависимости в массиве зависимостей из useEffect.

Рабочий код с // eslint-disable-next-line react-hooks/exhaustive-deps

export default function UsersList() {
   const [users, setUsers] = useState<User[]>([]);
   
   const { setError } = useContext(errorContext);
   const { isLoading, setIsLoading } = useContext(globalContext);
   
   useEffect(() => {
       if (users.length < 1) {
         fetchUsers();
       }
       // eslint-disable-next-line react-hooks/exhaustive-deps
     }, []);

     async function fetchUsers () {
       try {
         setIsLoading(true);
         const fetchedUsers = await api.getUsers();
         setUsers(fetchedUsers);
       } catch (error) {
         setError(error);
       } finally {
         setIsLoading(false);
       }
     }
}

Infinite код цикла

Я попытался написать его вот так: код запускает бесконечное l oop .. (потому что состояния постоянно меняются внутри функции и каждый раз запускает useEffect из-за объявленных зависимостей)

 useEffect(() => {
    async function fetchUsers () {
      try {
        setIsLoading(true);
        const fetchedUsers = await api.getUsers();
        setUsers(fetchedUsers);
      } catch (error) {
        setError(error);
      } finally {
        setIsLoading(false);
      }
    }

    if (users.length < 1) {
      fetchUsers();
    }
  }, [setIsLoading, setError, users]);

Я также попытался поместить fetchUsers() в массив зависимостей, но это не помогло.

Как правильно настроить вызов asyn c, когда компонент монтируется без необходимо использовать // eslint-disable-next-line react-hooks/exhaustive-deps?

1 Ответ

2 голосов
/ 20 июня 2020

Ваша fetchUsers функция воссоздается с каждым эффектом использования, запускающим рендеринг. Вы должны сохранить его ссылку одинаковой для всех рендеров, заключив его в useCallback, см. https://reactjs.org/docs/hooks-reference.html#usecallback

Кроме того, чтобы гарантировать, что мы вызываем этот useEffect только один раз (при первом рендеринге происходит) мы можем использовать useRef для хранения логического значения, которое предотвратит использование useEffect от бесконечного l oop

export default function UsersList() {
  const [users, setUsers] = useState<User[]>([]);
  
  const { setError } = useContext(errorContext);
  const { isLoading, setIsLoading } = useContext(globalContext);

  const fetchUsers = useCallback(async function () {
    try {
      setIsLoading(true);
      const fetchedUsers = await api.getUsers();
      setUsers(fetchedUsers);
    } catch (error) {
      setError(error);
    } finally {
      setIsLoading(false);
    }
  }, [setIsLoading, setUsers, setError]);

  // Added a ref here to ensure that we call this function only once in initial render
  // If you need to refetch the users on error, just call fetchUsers
  const isFetchedRef = useRef(false);
  useEffect(() => {
    if (!isFetchedRef.current) {
      isFetchedRef.current = true;
      fetchUsers();
    }
  }, [isLoading, fetchUsers]);
}
...