Выборка данных с помощью React-хаков очищает асинхронный обратный вызов - PullRequest
1 голос
/ 12 марта 2019

Я начал собирать некоторые из моих новых компонентов с новыми блестящими крючками React.Но я использовал много асинхронных API-вызовов в моих компонентах, где я также показываю загрузчик, пока данные извлекаются.Итак, насколько я понял концепцию, это должно быть правильно:

const InsideCompontent = props => {
   const [loading, setLoading] = useState(false);

   useEffect(() => {
     ...
     fetchData()
     ...
   },[])

   function fetchData() {
     setFetching(true);
     apiCall().then(() => {
       setFetching(false)
     })
   }
}

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

Есть ли какая-нибудь проверка, где я могу проверить, установлен ли компонент до того, как я вызову setFetching(false) в обратном вызове API?

Или я что-то здесь упускаю?

Вот рабочий пример: https://codesandbox.io/s/1o0pm2j5yq

РЕДАКТИРОВАТЬ: Здесь не было действительно проблемы.Вы можете попробовать это здесь: https://codesandbox.io/s/1o0pm2j5yq

Ошибка произошла из-за чего-то другого, поэтому с перехватчиками вам не нужно проверять, смонтирован ли компонент перед изменением состояния.

Еще одна причина, почему использовать его:)

Ответы [ 3 ]

2 голосов
/ 12 марта 2019

Вы можете использовать хук useRef для хранения любого изменяемого значения, которое вы хотите, так что вы можете использовать его для переключения переменной isMounted на false, когда компонент размонтирован, и проверьте,эта переменная true перед попыткой обновления состояния.

Пример

const { useState, useRef, useEffect } = React;

function apiCall() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Foo");
    }, 2000);
  });
}

const InsideCompontent = props => {
  const [state, setState] = useState({ isLoading: true, data: null });
  const isMounted = useRef(true);

  useEffect(() => {
    apiCall().then(data => {
      if (isMounted.current) {
        setState({ isLoading: false, data });
      }
    });

    return () => {
      isMounted.current = false
    };
  }, []);
  
  if (state.isLoading) return <div>Loading...</div>
  return <div>{state.data}</div>;
};

function App() {
  const [isMounted, setIsMounted] = useState(true);

  useEffect(() => {
    setTimeout(() => {
      setIsMounted(false);
    }, 1000);
  }, []);

  return isMounted ? <InsideCompontent /> : null;
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>
0 голосов
/ 03 мая 2019

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

https://www.npmjs.com/package/use-data-hook

(Вы также можете просто включить код , если вы не хотите весь пакет)

^ Также это преобразовывает в JavaScript, просто удаляя типы.

Эта статья слабо вдохновлена ​​ этой статьей , но с более широкими возможностями, поэтому, если вам не нужны манипуляции с данными, вы всегда можете использовать решение из этой статьи.

0 голосов
/ 12 марта 2019

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

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

Реагирует на жалобы и намеки на вас одновременно.Если компонент должен быть размонтирован, но имеется ожидающий сетевой запрос, его следует отменить.Возвращение функции из useEffect - это механизм для выполнения любой требуемой очистки ( docs ).

Построение на вашем примере с setTimeout:

const [fetching, setFetching] = useState(true);

useEffect(() => {
    const timerId = setTimeout(() => {
      setFetching(false);
    }, 4000);

    return () => clearTimeout(timerId)
  })

В случае, если компонент отключается до срабатывания обратного вызова, таймер очищается и setFetching не вызывается.

...