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