Извлечение данных с помощью перехватчиков React очищает асинхронный обратный вызов

Я начал создавать некоторые из своих новых компонентов с новыми и блестящими React Hooks. Но я использовал много асинхронных вызовов 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

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

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

Возможный дубликат Как отменить выборку на componentWillUnmount

r g 12.03.2019 14:43

@fard нет, это о хуках реакции, а не о обычных методах жизненного цикла реакции

Lorenz Weiß 12.03.2019 14:45

нет никакой реальной разницы и даже ответы были написаны в крючках

r g 12.03.2019 14:46

Не могли бы вы объяснить, почему отмена запроса не сработает для вас? Используете ли вы данные где-то еще?

Bear-Foot 12.03.2019 16:04
so with hooks you don't need to cancel the asynchronous callback that's what it does automatically. Это не будет сделано автоматически, мы должны сами позаботиться об очистке. (удаление прослушивателей событий, отмена сетевых запросов и т. д.)
Pavel Ye 12.03.2019 17:26
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
5
4 133
3

Ответы 3

Вы можете использовать хук 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>

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

Lorenz Weiß 12.03.2019 14:17

@Tholle Делать отменяемые обещания проще, чистое решение, предоставленное ребятами из React, и оно отлично работает с хуками reactjs.org/blog/2015/12/16/ismounted-antipattern.htmlgithub.com/facebook/react/issues/5465#issuecomment-157888325

r g 12.03.2019 14:59

@fard Метод isMounted() - это антипаттерн, да, но сохранение ссылки - это нормально. Введение отменяемой абстракции обещания является излишним во многих ситуациях.

Tholle 12.03.2019 15:01

@Tholle, я говорю о последнем разделе> В идеале, любые обратные вызовы должны быть отменены до размонтирования.

r g 12.03.2019 15:02

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

Warning: 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.

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

Основываясь на вашем примере с setTimeout:

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

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

    return () => clearTimeout(timerId)
  })

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

Тайм-аут был только для имитации времени запроса API. Но спасибо

Lorenz Weiß 12.03.2019 17:51

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

Pavel Ye 13.03.2019 19:09

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

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

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

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

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

Другие вопросы по теме