首页 > 解决方案 > React-Native - useEffect 导致无限循环

问题描述

我试图在我的组件中显示一些动态内容,但不知何故 useEffect 会导致无限循环。

可能是什么问题?

useEffect(() => {
    retrieveLocalData('following').then((contacts) => {
        setLocalData(JSON.parse(contacts));
    });
}, [getLocalData]);

async function retrieveLocalData(key) {
    try {
        return await AsyncStorage.getItem(key);
    } catch (error) {
        console.log(error);
    }
}
console.log('test'); // infinite

代码:https ://codepen.io/eneskul/pen/OJWEgmw

标签: react-nativeloopsinfinite

解决方案


更新的答案

无限循环是useEffect钩子更新触发钩子首先运行的相同值的结果。

下面是一个简单的例子来说明这个问题:

const [value, setValue] = useState({ foo: 'bar' });

useEffect(() => {
    Promise.resolve('{"foo":"bar"}').then((result) => {
        const newValue = JSON.parse(result);

        // `newValue` is a new object, even if its content is identical to `value`.
        setValue(newValue);
    });
}, [value]);

在这个例子中,当value设置时,它会导致useEffect钩子执行,它会异步更新value一个新对象,这将导致useEffect钩子再次执行,依此类推。即使对象的内容相同,JSON.parse调用也会创建一个具有新引用的新对象。

您可以通过在更新状态之前对两个对象进行深度相等检查来防止无限循环。使用类似Lodash 的isEqual函数可以让这变得非常简单。

useEffect(() => {
    Promise.resolve('{"foo":"bar"}').then((result) => {
        setValue((prev) => {
            const newValue = JSON.parse(result);

            // Do a deep comparison and only update state with new object if content is different.
            return isEqual(prev, newValue) ? prev : newValue;
        });
    });
}, [value]);

在这个例子中,只有当对象的内容不同时,对的引用value才会改变。

但是,这只能说明问题所在。我不确定您的问题的正确解决方案是什么,因为不清楚为什么组件只需要在状态更改时将数据从本地存储加载到状态中,但状态仅在从本地存储加载时才会更新。这里似乎存在“先有鸡还是先有蛋”的问题。感觉应该有其他东西应该触发将数据从本地存储加载到状态,而不是刚刚从本地存储加载到状态的数据。

上一个答案

这里可能的罪魁祸首是钩子getLocalData的依赖列表。useEffect如果这不是一个稳定的引用(即每次渲染时引用发生变化),那么它将导致useEffect钩子执行,然后触发状态更新,这将触发渲染,这将导致useEffect再次执行,从而启动整个事情重新来过。

在示例代码中,不清楚getLocalData来自哪里。无论它来自哪里,您都可以考虑用useCallback钩子包装它以创建稳定的引用。如果这只是一个错字并且是retrieveLocalData,那么这绝对是问题所在。因为retrieveLocalData在组件的渲染函数内部声明,所以它会在每次渲染时创建一个新的函数实例(带有新的引用)。

我只是将它移到useEffect钩子内并消除依赖关系。

useEffect(() => {
    AsyncStorage.getItem('following')
        .then((contacts) => {
            setLocalData(JSON.parse(contacts));
        })
        .catch((error) => {
            console.log(error);
        });
}, []);

推荐阅读