Очистка после AJAX функции в пользовательском React Hook - PullRequest
1 голос
/ 07 апреля 2020

Я написал пользовательскую ловушку React, которая использует jQuery для извлечения результатов по заданному URL-адресу через AJAX (это в контексте унаследованного приложения, поэтому необходимо использовать jQuery для AJAX ), API для которого слабо вдохновлен ax ios -hooks . Код для этого выглядит следующим образом (обратите внимание, что он включает аннотации потока):

//@flow
import { useCallback, useState, useEffect } from "react";
import type { jquery } from "jquery";

declare var $: jquery;

function useAjaxGet(url: string) {
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  const fetchData = useCallback(() => {
    setLoading(true);
    $.ajax({
      type: "GET",
      url: url,
      dataType: "json"
    }).done((data) => {
      setData(data);
      setLoading(false);
      setError(false);
    }).fail(() => {
      setLoading(false);
      setError(true);
    });
  }, [url]);

  useEffect(() => {
    fetchData();
  }, [url, fetchData]);

  return [{
    data: data,
    loading: loading,
    error: error
  }, fetchData];
}

export default useAjaxGet;

Теперь это работает для простых приложений, но рано или поздно выдает следующую ошибку:

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.

Я понимаю тот факт, что я должен вернуть функцию очистки, и что отказ сделать это вызывает утечку памяти. Тем не менее, я изо всех сил пытаюсь понять, что я могу сделать, чтобы разобраться в этом деле. Кто-нибудь может пролить свет на это?

1 Ответ

1 голос
/ 07 апреля 2020

Существует много способов очистки компонента. Основная концепция проста: вы не должны обновлять состояние в элементе umount реагировать .

Решение 1. Избежать обновления состояния вручную, используя переменную isMount

  const fetchData = useCallback(() => {
    let isMounted = true
    setLoading(true);
    $.ajax({
      type: "GET",
      url: url,
      dataType: "json"
    }).done((data) => {
      // Before updating the state, check if the element has been unmounted while waiting
      if (!isMounted) {
        return;
      }
      setData(data);
      setLoading(false);
      setError(false);
    }).fail(() => {
      // Same, check if the element has been unmounted while waiting
      if (!isMounted) {
        return;
      }
      setLoading(false);
      setError(true);
    });
    // Cleanup function
    return () => {
      isMounted = false;
    };
  }, [url]);

Вы можете увидеть это решение на Документах ретрансляции Facebook .

Демонстрация с использованием isMounting

Решение 2. Отмена запроса на отключение жизненного цикла ( AJAX jQuery)

Это позволит избежать выполнения всех обратных вызовов обещания. Гораздо чище! (В следующем примере используется jQuery v3)

  useEffect(() => {
    const xhr = new window.XMLHttpRequest();
    $.ajax({
      url: url,
      xhr: function() {
        return xhr;
      }
    })
    .then(response => {
      setState(response);
    })
    .catch(error => {
      console.log(error);
    });
    return () => {
      xhr.abort(); // Cancel the request on unmount
    };
  }, [url]);

Демонстрация с использованием Ajax отмена запроса

Решение 3: Отмена запроса на размонтирование жизненного цикла (выборка)

То же, что и в решении 2, но с использованием fetch, поскольку использование выше.

  useEffect(() => {
    const controller = new AbortController();
    const { signal } = controller;
    fetch(url, { signal })
      .then(res => res.json())
      .then(response => {
        setState(response);
      })
      .catch(error => {
        console.log(error);
      });
    return () => {
      controller.abort();
    };
  }, [url]);

Демонстрация с использованием AbortController

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...