Как избавиться от утечки памяти в реагирующем веб-приложении - PullRequest
1 голос
/ 09 апреля 2020

У меня есть этот код в моем ReactJS веб-приложении:

useEffect(() => {
    const fetchInfo = async () => {
      const res = await fetch(`${api}&page=${page}`);
      setLoading(true);
      try {
        const x = await res.json();
        if (page === 1) {
          setItems(x);
          setAutoplay(true);
        } else {
          setItems({
            hasMore: x.hasMore,
            vacancies: [...items.vacancies, ...x.vacancies],
          });
        }
      } catch (err){
        console.log(err);
      }
      setLoading(false);
    };
    fetchInfo();
  }, [page]);

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

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

Как отменить асинхронные задачи при очистке.

1 Ответ

1 голос
/ 09 апреля 2020

Я предполагаю, что setLoading - это функция, устанавливающая состояние после размонтирования вашего компонента, и поэтому выдает это предупреждение. Если да, то вам нужна функция очистки.

Функция, переданная в useEffect, может возвращать функцию, которая будет вызвана перед размонтированием компонента (вы можете думать об этом как о эквиваленте старый componentWillUnmount) - подробности здесь:

https://reactjs.org/docs/hooks-effect.html#example -using-hooks

Теперь вам, вероятно, нужен какой-то флаг, чтобы проверить, является ли он Вы можете безопасно вызывать setLoading, т. е. установить этот флаг на значение true по умолчанию, а затем установить его на false в функции возврата. Вот хорошая статья, которая должна помочь:

https://juliangaramendy.dev/use-promise-subscription/

Теперь я не проверял это, но по сути ваш код выглядел бы примерно так:

useEffect(() => {
    const fetchInfo = async () => {
        let isSubscribed = true;
        const res = await fetch(`${api}&page=${page}`);
        if (isSubscribed) setLoading(true);
        try {
            const x = await res.json();
            if (page === 1) {
                setItems(x);
                setAutoplay(true);
            } else {
                setItems({
                    hasMore: x.hasMore,
                    vacancies: [...items.vacancies, ...x.vacancies]
                });
            }
        } catch (err) {
            console.log(err);
        }
        if (isSubscribed) setLoading(false);

        return () => (isSubscribed = false);
    };
    fetchInfo();
}, [page]);
...