React-Native - useEffect 导致无限循环

时间:2021-04-15 17:05:12

标签: react-native loops infinite

我试图在我的组件中显示一些动态内容,但不知何故 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

1 个答案:

答案 0 :(得分:1)

更新答案

无限循环是 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,这将导致 {{1}钩子再次执行,依此类推。即使对象的内容相同,useEffect 调用也会创建一个具有新引用的新对象。

您可以通过在更新状态之前对两个对象进行深度相等性检查来防止无限循环。使用诸如 Lodash's isEqual function 之类的东西可以使这变得非常简单。

JSON.parse

在此示例中,对 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]); 的引用仅在对象的内容不同时才会更改。

然而,这只能说明问题所在。我不确定您的问题的正确解决方案是什么,因为尚不清楚为什么组件只需要在状态更改时将数据从本地存储加载到状态,而状态仅在从本地存储加载时更新。这里似乎有一个 "chicken or the egg" problem。感觉应该还有别的东西可以触发从本地存储加载数据到状态,而不是刚刚从本地存储加载到状态的数据。

上一个答案

此处可能的罪魁祸首是 value 挂钩的依赖项列表中的 getLocalData。如果那不是一个稳定的引用(即每次渲染时引用都会改变),那么它会导致 useEffect 钩子执行,然后触发状态更新,这将触发渲染,这将导致 {{ 1}} 再次执行,这将重新开始整个过程​​。

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

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

retrieveLocalData