Лучшая практика для предотвращения предупреждения об обновлении состояния для не подключенного компонента из обработчика - PullRequest
0 голосов
/ 15 июня 2019

Это общий вариант использования для извлечения и отображения данных из внешнего API (с помощью запросов XHR) при нажатии определенного компонента пользовательского интерфейса (например, <button />). Однако, если компонент тем временем был размонтирован, в консоли появляется следующее предупреждение:

Предупреждение. Невозможно выполнить обновление состояния React для отключенного компонента. Это не работает, но это указывает на утечку памяти в вашем приложении. Чтобы исправить, отмените все подписки и асинхронные задачи в функции очистки useEffect.

На самом деле, наиболее распространенное решение ( одобренное @ dan-abramov ), позволяющее избежать предупреждения, похоже, отслеживает состояние монтирования компонента с помощью функции возврата useEffect для очистки .

import React, { useState, useRef, useEffect } from "react";
import axios from "axios";

export default function PhotoList() {
  const mounted = useRef(true);
  const [photos, setPhotos] = useState(null);

  useEffect(() => {
    return () => {
      mounted.current = false;
    };
  }, []);

  function handleLoadPhotos() {
    axios("https://jsonplaceholder.typicode.com/photos").then(res => {
      if (mounted.current) {
        setPhotos(res.data);
      }
    });
  }

  return (
    <div>
      <button onClick={handleLoadPhotos}>Load photos</button>
      {photos && <p>Loaded {photos.length} photos</p>}
    </div>
  );
}

Однако, похоже, это приводит к ненужным накладным расходам, чтобы отслеживать состояние подключения и проверять его перед каждым обновлением состояния. Это становится особенно очевидным, когда вместо Обещаний используются Observables (где вы можете отписаться ).

Хотя вы действительно можете отписаться внутри useEffect, используя функцию очистки очень аккуратно:

useEffect(() => {
  // getPhotos() returns an observable of the photo list
  const photos$ = getPhotos().subscribe(setPhotos);
  return () => photos$.unsubscribe();
}, []);

Такая же интеллектуальная очистка невозможна в обработчике:

function handleLoadPhotos() {
  const photos$ = getPhotos().subscribe(setPhotos);
  // how to unsubscribe on unmounting?
}

Есть ли лучший способ избежать предупреждения без уродливого ручного отслеживания состояния монтажа с помощью useRef()? Есть ли хорошие подходы для этого при использовании Observables?

1 Ответ

1 голос
/ 15 июня 2019

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

Так что, вы должны искать другие пути.

Я всегда делаю асинхронные операции в редуксе.

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

На самом деле, вы должны писать декларативные компоненты пользовательского интерфейса, которые визуализируются для данного реквизита. Таким образом, ваши данные должны быть за пределами вашей логики компонентов тоже.

...