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

0 263

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

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

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

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

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

React JS. Основы

Изучите основы ReactJS на практическом примере по созданию учебного веб-приложения

Получить курс сейчас!

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

1. Проблема

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

App component:

JavaScript // 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> ); }

12345678910111213141516171819202122 // 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:

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

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

Users component:

JavaScript // 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> ); };

123456789101112131415161718192021222324252627282930 // 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>  );};

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

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

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

React JS. Основы

Изучите основы ReactJS на практическом примере по созданию учебного веб-приложения

Получить курс сейчас!

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

2. Очистка

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

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

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

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

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

JavaScript // 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(); });

1234567891011121314151617181920212223 // 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, который получает запрос на загрузку сведений о конкретном сотруднике на основе идентификатора, указанного в реквизитах.

JavaScript // 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> ); };

123456789101112131415161718192021222324252627282930313233343536 // 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). Например, рассмотрим счетчик, который автоматически увеличивается каждую секунду.

JavaScript // 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>; }

12345678910111213141516 // 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. Очистка подписки

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

JavaScript // 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(); },); // … }

1234567891011121314 // 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() позволяет нам выполнять различные виды побочных эффектов после рендеринга компонента, а затем очищать их в зависимости от их типа.

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

Оставьте ответ

Ваш электронный адрес не будет опубликован.