简体   繁体   中英

React memory leak warning

Inside my application, one of my components unfortunately is sending the Can't perform a React state update on an unmounted component warning. It is driving me crazy at this moment. I was trying to locate the exact place where it might be happening but unfortunately unsuccessfully.

Any ideas of what could be causing it or how to find the source of the problem? I would appreciate any help or direction where to look.

The console:

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.
CurrencySelect@http://localhost:3000/static/js/main.chunk.js:4444:24
CurrencySelect
Controller@http://localhost:3000/static/js/vendors~main.chunk.js:118828:29
CurrencySelect@http://localhost:3000/static/js/main.chunk.js:10270:26

Component:

import React, { useState, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';

import CountrySelectOption from '../CountrySelectOption/CountrySelectOption';
import CountrySelectValue from '../CountrySelectValue/CountrySelectValue';
import Select from '../../../custom/ChakraReactSelect/ChakraReactSelect';

import {
  getCurrencies,
  getCurrenciesOptionsData
} from '../../../../services/currencies/currenciesServices';

const currencySelectComponent = {
  Option: CountrySelectOption,
  SingleValue: CountrySelectValue
};

function CurrencySelect({ size = 'lg', isSearchable = true, ...other }) {
  const [currenciesOptions, setCurrenciesOptions] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    let isMounted = true;
    const prepareCurrenciesOptions = async () => {
      try {
        if (isMounted) {
          setIsLoading(true);
          const currencies = await (await getCurrencies()).json();
          const preparedData = getCurrenciesOptionsData(currencies);
          setCurrenciesOptions(preparedData);
          setIsLoading(false);
        }
      } catch (err) {
        console.error(err);
      }
    };

    prepareCurrenciesOptions();
    return () => {
      isMounted = false;
    };
  }, []);

  return (
    <Select
      {...other}
      size={size}
      isSearchable={isSearchable}
      isClearable
      placeholder={isLoading ? 'Loading currencies...' : 'Select currency...'}
      options={currenciesOptions}
      components={currencySelectComponent}
      isLoading={isLoading}
      isDisabled={isLoading}
      openMenuOnFocus={true}
    />
  );
}

const CurrencySelectRef = React.forwardRef((props, _) => (
  <CurrencySelect {...props} />
));

CurrencySelectRef.displayName = 'CurrencySelect';

export default CurrencySelectRef;

CurrencySelect.propTypes = {
  size: PropTypes.string,
  isSearchable: PropTypes.bool,
  ref: PropTypes.func
};

The answer was to move down the isMounted check after await of getCurrencies(), like this:

old code:

useEffect(() => {
    let isMounted = true;
    const prepareCurrenciesOptions = async () => {
      try {
        if (isMounted) {
          setIsLoading(true);
          const currencies = await (await getCurrencies()).json();
          const preparedData = getCurrenciesOptionsData(currencies);
          setCurrenciesOptions(preparedData);
          setIsLoading(false);
        }
      } catch (err) {
        console.error(err);
      }
    };

    prepareCurrenciesOptions();
    return () => {
      isMounted = false;
    };
  }, []);

new code:

  useEffect(() => {
    let isMounted = true;
    const prepareCurrenciesOptions = async () => {
      try {
        setIsLoading(true);
        const currencies = await (await getCurrencies()).json();
        // Currncies might still be pending when te component is already unmounted
        // This is to prevent the state updating on already unmounted component
        if (isMounted) {
          const preparedData = getCurrenciesOptionsData(currencies);
          setCurrenciesOptions(preparedData);
          setIsLoading(false);
        }
      } catch (err) {
        console.error(err);
      }
    };

    prepareCurrenciesOptions();
    return () => {
      isMounted = false;
    };
  }, []);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM