DevGang
Авторизоваться

Элегантное решение для утечек памяти в React 

При работе с асинхронными вызовами, например вызовами API, вы могли столкнуться с этой ошибкой:

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.

GIF стоит тысячи слов ...

Это небольшая страница, которая имитирует некоторую асинхронную логику при загрузке, а затем соответствующим образом обновляет представление. Здесь я размонтирую компонент до завершения асинхронной работы и вызову ошибку. (Я взял этот пример из этого поста StackOverFlow)

Это вызвано этим кодом:

function Example() {
  const [text, setText] = useState("waiting...");

  useEffect(() => {
    simulateSlowNetworkRequest().then(() => {
      setText("done!"); // ⚠️ what if the component is no longer mounted ?
      // => Warning: Can't perform a React state update on an unmounted component.
    });
  }, []);

  return <h2>{text}</h2>;
}

Когда я столкнулся с этой проблемой, я нашел несколько решений, наиболее часто используемое из них:

function OtherExample() {
    const [text, setText] = useState("waiting...");

  useEffect(() => {
    let isMounted = true; // 👈
    simulateSlowNetworkRequest().then(() => {
      if (!isMounted) { // 👈
        setText("done!"); // no more error
      }
    });
    return () => {
      isMounted = false; // 👈
    };
  }, []);

  return <h2>{text}</h2>;
}

Но это требует, чтобы вы добавили довольно много вещей в ваш компонент, имея дело с переменной isMounted повсюду ...

Есть и другие интересные решения, например, возможность отмены ваших обещаний.

Вы сказали мне, что будет элегантное решение!

Я не лгал! Решение, которое я придумал, - это очень простой способ. Он работает так же, как React useState, но в основном проверяет, смонтирован ли компонент перед обновлением состояния!

Вот пример измененного кода:

function OtherExample() {
  const [text, setText] = useStateIfMounted("waiting..."); // 👈

  React.useEffect(() => {
    simulateSlowNetworkRequest().then(() => {
      setText("done!"); // no more error
    });
  }, [setText]);

  return <h2>{text}</h2>;
}

TLDR

Используйте ловушку useStateIfMounting, которая обновит состояние, только если ваш компонент смонтирован! 🚀

Источник:

#React
Комментарии 1
Юрий Кононов 05.08.2022 в 14:27

гавна кусок этот реакт. Что это за фреймворк такой где я должен сам постоянно утечки памяти затыкать, вместо того что бы сосредоточиться на самой логике работы приложения? Везде надо пихать какие то костыли, я уже не говорю об официальных реактовских костылях таких как useCallback, useMemo и memo. Эти костыли запилили тогда когда и придумали уродливые функциональные компоненты, а запилили эти хуки что бы залатать фундоментальные дыры этого недо-фреймворка. Функциональные компоненты придумали тоже из за фундаментальной уродливости реакта

Чтобы оставить комментарий, необходимо авторизоваться