Как отменить подписку в асинхронном обещании, чтобы избежать утечки памяти в Reactjs - PullRequest
3 голосов
/ 30 сентября 2019

В моем компоненте React у меня есть запрос async, который отправляет действие в мое хранилище Redux, которое вызывается в хуке useEffect:

    const loadFields = async () => {
        setIsLoading(true);
        try {
            await dispatch(fieldsActions.fetchFields(user.client.id));
        } catch (error) {
            setHasError(true);
        }
        setIsLoading(false);
    }
    useEffect(() => { if(isOnline) { loadFields() } }, [dispatch, isOnline]);

Действие запрашивает данные через запрос выборки. :

    export const fetchFields = clientId => {
        return async dispatch => {
            try {
                const response = await fetch(
                    Api.baseUrl + clientId + '/fields',
                    { headers: { 'Apiauthorization': Api.token } }
                );

                if (!response.ok) {
                    throw new Error('Something went wrong!');
                }

                const resData = await response.json();
                dispatch({ type: SET_FIELDS, payload: resData.data });
            } catch (error) {
                throw error;
            }
        }
    };

    export const setFields = fields => ({
        type    : SET_FIELDS,
        payload : fields
    });

Когда это отображается в приложении React, выдается следующее предупреждение:

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 %s.%s, a useEffect cleanup function

Я полагаю, что это происходит, потому что обещание неиметь функцию «очистки». Но я не уверен, где это разместить? Должен ли я иметь некоторую логику в LoadFields()? Или это должно быть сделано в useEffect хуке?

Ответы [ 2 ]

2 голосов
/ 30 сентября 2019

Этот учебник , который поможет вам решить вашу проблему.

Быстрый пример: с обещаниями

function BananaComponent() {

  const [bananas, setBananas] = React.useState([])

  React.useEffect(() => {
    let isSubscribed = true
    fetchBananas().then( bananas => {
      if (isSubscribed) {
        setBananas(bananas)
      }
    })
    return () => isSubscribed = false
  }, []);

  return (
    <ul>
    {bananas.map(banana => <li>{banana}</li>)}
    </ul>
  )
}

Быстрый пример: с асинхронным ожиданием (нелучший, но это должно работать с анонимной функцией)

function BananaComponent() {

  const [bananas, setBananas] = React.useState([])

  React.useEffect(() => {
    let isSubscribed = true
    async () => {
      const bananas = await fetchBananas();
      if (isSubscribed) {
        setBananas(bananas)
      }
    })();

    return () => isSubscribed = false
  }, []);

  return (
    <ul>
    {bananas.map(banana => <li>{banana}</li>)}
    </ul>
  )
}
1 голос
/ 30 сентября 2019

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

const [data, setData] = useState();
useEffect(() => {
  const controller = new AbortController();
  const fetchData = async () => {
    try {
      const apiData = await fetch("https://<yourdomain>/<api-path>",
                               { signal: controller.signal });
      setData(apiData);
    } catch (err) {
      if (err.name === 'AbortError') {
        console.log("Request aborted");
        return;
      }
    }
  };

  fetchData();
  return () => {
    controller.abort();
  }
});

Второй выпуск Этот код

return async dispatch => {

не будет работать, поскольку ни диспетчеризация, ни хранилище Redux не поддерживают асинхронные действия. Наиболее гибкий и эффективный способ решения этой проблемы - использовать промежуточное программное обеспечение, например redux-saga . Промежуточное программное обеспечение позволяет:

  • отправлять «обычные» действия по синхронизации в хранилище Redux.
  • перехватывает эти синхронизирующие действия и в ответ делает один или несколько асинхронных вызовов, делая все, что вы хотите.
  • ожидает завершения асинхронных вызовов и в ответ отправляет одно или несколько синхронизирующих действий в хранилище Reduxлибо оригинальные, которые вы перехватили, либо разные.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...