当更新不在依赖列表中的变量时调用 useEffect(导致无限循环)
useEffect called when a variable not in the dependancy list is updated (causing an infinite loop)
我正在尝试在状态更改时执行 ajax 调用,然后将另一个状态设置为该 ajax 调用的结果。
const [plc, setPlc] = useState(null);
const [tags, setTags] = useState([]);
...
useEffect(()=>{
if(plc != null) {
myAjaxPromiseFunction(plc).catch((err)=>{
console.log(err);
}).then((tags)=>{
setTags(tags);
});
}
}, [plc]);
出于某种原因,这会导致无限循环。但是,当我删除 setTags(tags);
语句时,它会按预期工作。似乎 setTags
函数导致效果挂钩更新,但 tags
不是此效果挂钩的依赖项的一部分。
仅供参考,tags
变量应该是一个对象列表。
编辑:
我的代码中还有一个 useEffect
。
useEffect(() => { // Update which tags are logged
if(plc != null) {
anotherAjaxFunction(plc, tags).catch(err => {
toaster.show({
message: err,
intent: "danger"
});
}).then(tags => {
setTags(tags);
});
}
}, [plc, updateLoggedFlag]);
然而,这个useEffect
不依赖于tags
,如果我删除这段代码,问题仍然存在。
旁注: updateLoggedFlag
是我计划用来强制效果根据需要更新的变量,但我还没有在任何地方使用它.
另一个编辑: Link 使用代码沙箱进行复制:https://codesandbox.io/s/cranky-ives-3xyrq?file=/src/App.js
有什么想法吗?感谢您的帮助。
我认为您的代码存在的问题是它没有在依赖数组中包含标签。从概念上讲,这应该可以正常工作,正如其他开发人员之前提到的那样,但是 React 文档指出,回调中使用的所有状态变量和道具都应该存在于依赖数组中,否则会导致错误。在这里看看。 Hooks FAQ
简而言之:将 TagColumn
移到 App
之外,因为它是 here。
问题与效果声明无关,而是由于组件声明。
在您的沙箱中,TagColumn
是在 App
中定义的。这意味着每次渲染 App
时(特别是每次更改其状态时),都会为 const TagColumn
分配不同的值。 React 无法每次都看到它是相同的组件 - 毕竟,它并不完全相同,因为捕获的范围在变化。
因此,App
呈现一个新的 TagColumn
。 React 看到组件是从头开始创建的,渲染它,特别是调用它的钩子,包括每个 useEffect
。在 useEffect
中,您使用回调来更改 App
的状态。 App
重新渲染,创建 另一个 组件 TagColumn
,这是它生命中的第一次再次渲染,调用每个 useEffect
... 和循环继续。
一般来说,您不应在功能组件范围内捕获任何非常量。相反:
- 如果该值是由外部上下文生成的(
App
,在这种情况下)——将其作为道具传递;
- 如果该值由组件本身生成 - 根据需要使用
useState
、useRef
或其他挂钩。
我正在尝试在状态更改时执行 ajax 调用,然后将另一个状态设置为该 ajax 调用的结果。
const [plc, setPlc] = useState(null);
const [tags, setTags] = useState([]);
...
useEffect(()=>{
if(plc != null) {
myAjaxPromiseFunction(plc).catch((err)=>{
console.log(err);
}).then((tags)=>{
setTags(tags);
});
}
}, [plc]);
出于某种原因,这会导致无限循环。但是,当我删除 setTags(tags);
语句时,它会按预期工作。似乎 setTags
函数导致效果挂钩更新,但 tags
不是此效果挂钩的依赖项的一部分。
仅供参考,tags
变量应该是一个对象列表。
编辑:
我的代码中还有一个 useEffect
。
useEffect(() => { // Update which tags are logged
if(plc != null) {
anotherAjaxFunction(plc, tags).catch(err => {
toaster.show({
message: err,
intent: "danger"
});
}).then(tags => {
setTags(tags);
});
}
}, [plc, updateLoggedFlag]);
然而,这个useEffect
不依赖于tags
,如果我删除这段代码,问题仍然存在。
旁注: updateLoggedFlag
是我计划用来强制效果根据需要更新的变量,但我还没有在任何地方使用它.
另一个编辑: Link 使用代码沙箱进行复制:https://codesandbox.io/s/cranky-ives-3xyrq?file=/src/App.js
有什么想法吗?感谢您的帮助。
我认为您的代码存在的问题是它没有在依赖数组中包含标签。从概念上讲,这应该可以正常工作,正如其他开发人员之前提到的那样,但是 React 文档指出,回调中使用的所有状态变量和道具都应该存在于依赖数组中,否则会导致错误。在这里看看。 Hooks FAQ
简而言之:将 TagColumn
移到 App
之外,因为它是 here。
问题与效果声明无关,而是由于组件声明。
在您的沙箱中,TagColumn
是在 App
中定义的。这意味着每次渲染 App
时(特别是每次更改其状态时),都会为 const TagColumn
分配不同的值。 React 无法每次都看到它是相同的组件 - 毕竟,它并不完全相同,因为捕获的范围在变化。
因此,App
呈现一个新的 TagColumn
。 React 看到组件是从头开始创建的,渲染它,特别是调用它的钩子,包括每个 useEffect
。在 useEffect
中,您使用回调来更改 App
的状态。 App
重新渲染,创建 另一个 组件 TagColumn
,这是它生命中的第一次再次渲染,调用每个 useEffect
... 和循环继续。
一般来说,您不应在功能组件范围内捕获任何非常量。相反:
- 如果该值是由外部上下文生成的(
App
,在这种情况下)——将其作为道具传递; - 如果该值由组件本身生成 - 根据需要使用
useState
、useRef
或其他挂钩。