【问题标题】:How do I clear out an Image.getSize request when unmounting a React Native component?卸载 React Native 组件时如何清除 Image.getSize 请求?
【发布时间】:2020-03-24 15:20:32
【问题描述】:

我有一个 React Native 组件,它为组件中的每个图像发出 Image.getSize 请求。然后在Image.getSize 请求的回调中,我为我的组件设置了一些状态。一切正常,但问题是用户可能会在一个或多个Image.getSize 请求响应之前从使用该组件的屏幕转换,这会导致“无操作内存泄漏”错误弹出up 因为我试图在组件卸载后更改状态。

所以我的问题是:如何在卸载组件后阻止Image.getSize 请求尝试修改状态?这是我的组件代码的简化版本。谢谢。

const imgWidth = 300; // Not actually static in the component, but doesn't matter.

const SomeComponent = (props) => {
    const [arr, setArr] = useState(props.someData);

    const setImgDimens = (arr) => {
        arr.forEach((arrItem, i) => {
            if (arrItem.imgPath) {
                const uri = `/path/to/${arrItem.imgPath}`;

                Image.getSize(uri, (width, height) => {
                    setArr((currArr) => {
                        const newWidth = imgWidth;
                        const ratio = newWidth / width;
                        const newHeight = ratio * height;

                        currArr = currArr.map((arrItem, idx) => {
                            if (idx === i) {
                                arrItem.width = newWidth;
                                arrItem.height = newHeight;
                            }

                            return arrItem;
                        });

                        return currArr;
                    });
                });
            }
        });
    };

    useEffect(() => {
        setImgDimens(arr);

        return () => {
            // Do I need to do something here?!
        };
    }, []);

    return (
        <FlatList
            data={arr}
            keyExtractor={(arrItem) => arrItem.id.toString()}
            renderItem={({ item }) => {
                return (
                    <View>
                        { item.imgPath ?
                            <Image
                                source={{ uri: `/path/to/${arrItem.imgPath}` }}
                            />
                            :
                            null
                        }
                    </View>
                );
            }}
        />
    );
};

export default SomeComponent;

【问题讨论】:

    标签: reactjs react-native react-component react-state react-component-unmount


    【解决方案1】:

    我必须实现类似的东西,我首先初始化一个名为isMounted 的变量。

    当组件挂载时设置为true,当组件卸载时设置为false

    在调用setImgDimens 之前,会检查组件是否已安装。如果没有,它不会调用该函数,因此不会更新状态。

    const SomeComponent = (props) => {
      const isMounted = React.createRef(null);
      useEffect(() => {
        // Component has mounted
        isMounted.current = true;
    
        if(isMounted.current) {
          setImgDimens(arr);
        }
    
        return () => {
          // Component will unmount
          isMounted.current = false;
        }
      }, []);
    }
    

    编辑:这是对我有用的答案,但为了它的价值,我不得不将 isMounted 变量移动到 外部 SomeComponent 函数让它工作。此外,您可以只使用常规变量而不是 createRef 来创建引用等。

    基本上,以下对我有用:

    let isMounted;
    
    const SomeComponent = (props) => {
        const setImgDimens = (arr) => {
            arr.forEach((arrItem, i) => {
                if (arrItem.imgPath) {
                    const uri = `/path/to/${arrItem.imgPath}`;
    
                    Image.getSize(uri, (width, height) => {
                        if (isMounted) { // Added this check.
                            setArr((currArr) => {
                                const newWidth = imgWidth;
                                const ratio = newWidth / width;
                                const newHeight = ratio * height;
    
                                currArr = currArr.map((arrItem, idx) => {
                                    if (idx === i) {
                                        arrItem.width = newWidth;
                                        arrItem.height = newHeight;
                                    }
    
                                    return arrItem;
                                });
    
                                return currArr;
                            });
                        }
                    });
                }
            });
        };
    
        useEffect(() => {
            isMounted = true;
            setImgDimens(arr);
    
            return () => {
                isMounted = false;
            }
        }, []);
    };
    

    【讨论】:

    • 聪明又合情合理。这种事情对我来说总是很笨拙,让我觉得框架本身应该更好地管理这种事情,但仍然有效!谢谢。
    • 快速提问,Dan:为什么要创建一个 React ref 并使用 .current 而不是在组件卸载时更新为 false 的常规状态变量?
    • 我同意你的看法,说实话,我不确定这是否是最佳做法,但它很容易奏效。在这篇文章中,记录了这在某种程度上是一种反模式github.com/facebook/react/issues/12111#issuecomment-361254441
    • 再次感谢,丹。关于您为什么使用createRef 而不是useState 作为isMounted 变量的任何评论?谢谢。
    • @HartleySan 不错的编辑,如果您在组件内部声明变量,则会提醒您在每次渲染时对 useEffect 挂钩内部变量的赋值都会丢失。使用ref 有助于缓解这种情况。但也将变量放在外面也可以完成这项工作
    猜你喜欢
    • 2020-05-06
    • 2015-07-26
    • 2022-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多