Элегантное решение для утечек памяти в 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>;
}
use local variable to avoid setState on unmouted component using react, react-dom, react-scripts, use-state-if-mounted
TLDR
Используйте ловушку useStateIfMounting, которая обновит состояние, только если ваш компонент смонтирован! 🚀