无法对卸载的组件执行 React 状态更新 - 内存泄漏?

时间:2021-04-02 20:47:09

标签: reactjs react-native use-effect

我曾尝试在 mounted = useRef()let mounted.current = true 的开头使用 useEffectreturn () => mounted.current = false,但仍然收到此错误消息:

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.
    in NameDetails (at TitleRow.js:114)
    in RCTView (at View.js:34)
    in View (at TitleRow.js:113)
    in titleRow (at TodayTitleList.js:154)
    in RCTView (at View.js:34)
    in View (at VirtualizedList.js:2015)
    in VirtualizedListCellContextProvider (at VirtualizedList.js:2030)
    in CellRenderer (at VirtualizedList.js:807)
    in RCTScrollContentView (at ScrollView.js:1107)
    in RCTScrollView (at ScrollView.js:1213)
    in ScrollView (at ScrollView.js:1264)
    in ScrollView (at VirtualizedList.js:1250)
    in VirtualizedListContextProvider (at VirtualizedList.js:1080)
    in VirtualizedList (at FlatList.js:620)
    in FlatList (at TodayTitleList.js:149)
    in RCTSafeAreaView (at SafeAreaView.js:51)
    in SafeAreaView (at TodayTitleList.js:136)

查看堆栈跟踪,我猜测错误出在 NameDetails 文件中,并且涉及其中的 useEffect。是这样吗?

以下是我的代码:

const NameDetails = ({ title }) => {
  const [color, setColor] = useState('#000');

  useEffect(() => {
    //let mounted = true; 
    const getColor = async (id) => {
      const chosenColor = await getColorFortitle(id);
      setColor(chosenColor);
    //return () => { mounted = false };
    };

    getColor(title.id);
  });

  return (
    <View>
        ...
    </View>
  );
};

export default NameDetails;

更多信息:当我注销并尝试再次登录时会发生此错误,并且仅在某些时候发生。

3 个答案:

答案 0 :(得分:0)

您正在使用 useEffect 每次重新渲染组件: 添加 empty dependency to useEffect to run only on first render

const mounted = useRef(false);

useEffect(() => mounted.current = true, []);


useEffect(() => {
    const getColor = async (id) => {
     const chosenColor = await getColorFortitle(id);
     setColor(chosenColor);
    }

    if(mounted.current)
      getColor(title.id);

    return () => { mounted.current = false };
}, []);

答案 1 :(得分:0)

请检查我的代码 希望对你有帮助。

const NameDetails = ({ title }) => {
  const [color, setColor] = useState('#000');
  const [mounted, setMount] = useState(true);

  useEffect(() => { 
    const getColor = async (id) => {
      const chosenColor = await getColorFortitle(id);
      setColor(chosenColor);
      return () => { setMount = false };
    };

    getColor(title.id);
  });

  return (
    <View>
        ...
    </View>
  );
};

export default NameDetails;

我只使用一种状态。因此,您可以使用状态更改值。 运行您的代码。

答案 2 :(得分:0)

const NameDetails = ({ title }) => {
  const [color, setColor] = useState('#000');

  useEffect(() => {
    let mounted = true; 
    
    const getColor = async (id) => {
      const chosenColor = await getColorFortitle(id);
      mounted && setColor(chosenColor);
    };

    getColor(title.id);

    return () => { mounted = false };
  }, [title.id]);

  return (
    <View>
        ...
    </View>
  );
};

更理想的方法:

const NameDetails = ({ title }) => {
  const isMounted = React.useRef(true);

  React.useEffect(() => () => (isMounted.current = false), []);
  
  const [color, setColor] = useState('#000');

  useEffect(() => {
    const getColor = async (id) => {
      const chosenColor = await getColorFortitle(id);
      isMounted.current && setColor(chosenColor);
    };

    getColor(title.id);
  }, [title.id]);

  return (
    <View>
        ...
    </View>
  );
};

或者,如果您不确定那里发生了什么,您可以尝试将我的库与基于生成器的异步钩子一起使用,这样您就不必担心卸载了:

import React, { useState } from "react";
import { useAsyncEffect } from "use-async-effect2";

const NameDetails = ({ title }) => {     
  const [color, setColor] = useState('#000');

  useAsyncEffect(function*(){
      const chosenColor = yield getColorFortitle(title.id);
      setColor(chosenColor);
  }, [title.id]);

  return (
    <View>
        ...
    </View>
  );
};