Главная » Статьи » Как очистить побочные эффекты в React

Как очистить побочные эффекты в React

Как очистить побочные эффекты в React

От автора: в React мы используем useEffec t, когда нам нужно что-то сделать после рендеринга компонента или когда нам нужно применить побочные эффекты. Побочным эффектом может быть выборка данных с удаленного сервера, чтение или запись в локальное хранилище, настройка слушателей событий или настройка подписки.

UseEffect() позволяет нам управлять жизненными циклами компонентов внутри функциональных компонентов. Хук useEffect() можно рассматривать как комбинацию componentDidMount, componentDidUpdate и componentWillUnmount.

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

Когда побочный эффект завершается, он пытается обновить состояние уже размонтированного компонента. В результате выводится предупреждение React:

Как очистить побочные эффекты в React

Предупреждение об утечке памяти

В этом посте мы рассмотрим, когда появляется указанное выше предупреждение и как правильно очищать побочные эффекты в React, чтобы избежать утечек памяти.

1. Проблема

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

App component:

// useEffectApp.js function App() { const [display, setDisplay] = useState("users"); return ( <div className='App'> <button onClick={() => { setDisplay("users"); }}> display users </button> <button onClick={() => { setDisplay("posts"); }}> display hello message </button> <>{display === "users" ? <Users /> : <Hello />}</> </div> );
}

Hello component:

// hello.js export default function Hello() { return ( <p> Hello, World !! </p> );
}

Users component:

// useEffectUsersNotClean.js export default function Users() { const [list, setList] = useState(null); useEffect(() => { (function () { try { fetch(`https://jsonplaceholder.typicode.com/users`) .then((response) => response.json()) .then((json) => setList(json)); } catch (e) { // Handle the error } })(); }); return ( <div> {list === null ? ( <p>Loading users...</p> ) : ( <> {list.map((item) => { return <div key={item.id}>{item.name}</div>; })} </> )} </div> );
};

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

Причина этого предупреждения в том, что компонент уже размонтирован, но побочный эффект пытается обновить состояние размонтированного компонента.

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

2. Очистка

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

useEffect(() => { // the side effect takes place here. return () => { // the cleanup function } // dependencies array
}, [])

2-1. Очистка запросов на выборку

Сначала мы создаем контроллер, который позволяет нам прерывать запросы DOM, затем мы подключаем контроллер к запросу на выборку. И, наконец, функция очистки t прерывает запрос в случае размонтирования компонента.

// useEffectCleanUpFetchRequests.js useEffect(() => { //create a controller let controller = new AbortController(); (async () => { try { const response = await fetch( `https://jsonplaceholder.typicode.com/posts`, { // connect the controller with the fetch request signal: controller.signal, }, ); setList(await response.json()); controller = null; } catch (e) { // Handle the error } })(); //aborts the request when the component umounts return () => controller?.abort(); });

2-2. Очистка при обновлении свойств или состояния

Могут возникнуть случаи, когда мы хотим прервать запрос на выборку, когда побочный эффект зависит от свойства или значения состояния. И, как я упоминал ранее, хук useeffect() может обрабатывать эти случаи. Например, рассмотрим следующий компонент User, который получает запрос на загрузку сведений о конкретном сотруднике на основе идентификатора, указанного в реквизитах.

// UseEffectCleanUpOnPropOrStateUpdate.js export default function User({ id }) { const [user, setUser] = useState(null); useEffect(() => { let controller = new AbortController(); (async () => { try { const response = await fetch( `https://jsonplaceholder.typicode.com/users/${id}`, { signal: controller.signal, }, ); setUser(await response.json()); controller = null; } catch (e) { // Handle the error } })(); // clean up function return () => controller?.abort(); // add a dependency array }, [id]); return ( <div> {user === null ? ( <p>Loading user's data ...</p> ) : ( <div key={user.id}>{user.name}</div> )} </div> );
};

2-3. Очистка таймеров

При использовании функций таймера мы можем очистить их при размонтировании с помощью специальной функции clearTimeout (timerId). Например, рассмотрим счетчик, который автоматически увеличивается каждую секунду.

// useEffectCleanUpTimersWhenTheComponentUnmounts.js export default function AutoIncrementaedCounter() { const [counterValue, setCounterValue] = useState(0); useEffect(() => { //increments the counter value by 1 every 3 secends let timerId = setTimeout(() => { setCounterValue(counterValue + 1); timerId = null; }, 3000); // cleanup the timmer when component unmout return () => clearTimeout(timerId); }); return <p>{counterValue}</p>;
}

2–4. Очистка подписки

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

// useEffectCleanUpWebSockets.js export default function Component() { const [url] = useState(""); useEffect(() => { const webSocket = new WebSocket(url); // do stuff here // clean up when component unmount return () => webSocket.close(); },); // ...
}

3. ЗАКЛЮЧЕНИЕ

Некоторые эффекты могут потребовать очистки, чтобы избежать утечки памяти. UseEffect() позволяет нам выполнять различные виды побочных эффектов после рендеринга компонента, а затем очищать их в зависимости от их типа.

Источник: medium.com

Редакция: Команда webformyself.

Читайте нас в Telegram, VK, Яндекс.Дзен