一、谬误复现
开发环境报如下谬误。
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
Call Stack
checkForNestedUpdates
website/./node_modules/react-dom/cjs/react-dom.development.js:4013:321
scheduleUpdateOnFiber
website/./node_modules/react-dom/cjs/react-dom.development.js:3606:187
dispatchAction
website/./node_modules/react-dom/cjs/react-dom.development.js:2689:115
eval
website/./src/components/FileUpload.jsx:73:7
invokePassiveEffectCreate
website/./node_modules/react-dom/cjs/react-dom.development.js:3960:1047
HTMLUnknownElement.callCallback
website/./node_modules/react-dom/cjs/react-dom.development.js:657:119
Object.invokeGuardedCallbackDev
website/./node_modules/react-dom/cjs/react-dom.development.js:677:45
invokeGuardedCallback
website/./node_modules/react-dom/cjs/react-dom.development.js:696:126
flushPassiveEffectsImpl
website/./node_modules/react-dom/cjs/react-dom.development.js:3968:212
unstable_runWithPriority
website/./node_modules/scheduler/cjs/scheduler.development.js:465:16
二、谬误排查
- 通过正文代码的形式,发现出问题的中央,是
Assets
组件中援用的FileUpload
出了问题。正好最近也批改过FileUpload
组件。 - 通过sourcetree比照git记录,看
FileUpload
组件被批改了什么?如下图。 - 再比照谬误提醒中的形容,其中
componentWillUpdate or componentDidUpdate
,揣测就是指新增的useEffect
代码片断。 - 将上述
useEffect
代码片断正文掉,果然谬误隐没。
三、起因剖析
useEffect
的个性表明,只有initFiles
产生了扭转,46-48行代码就会执行。
既然上述useEffect
代码片断事实上造成了死循环,就还阐明了一点:
setFileList(initFiles)
扭转了initFiles
,才使得useEffect
中的函数再次被调用。
那么,initFiles
到底是经验了怎么的变动,才使得调用可能周而复始地产生呢?
输入fileList
和initFiles
:
console.log(fileList === initFiles)
能够发现,只有第一次render时输入true
,后续全副是false
。
- 第一次输入
true
,表明useState
的入参为array
时,只是简略的赋值关系,fileList
和initFiles
指定了同一个内存地址。 setFileList
函数实际上是做了一次浅拷贝,而后赋值给fileList
,扭转了fileList
的内存指向,也就是扭转了最新initFiles
的内存指向。同时React保留了之前initFiles
的值用来做依赖比照。useEffect
在比照援用类型的依赖,比方object/array
时,采纳的是简略的===
操作符,也就是说比拟内存地址是否统一。- 前后两次
initFiles
尽管外部数据雷同,但内存指向不同,就被useEffect
认为【依赖产生了扭转】,从而导致了死循环。
四、解决方案
-
尽量不间接应用object或者array作为依赖项,而是应用值类型来代替援用类型
useEffect(() => { //... }, [initFiles.length])
五、拓展
是不是在调用useState
时,拷贝initFiles
就能够了呢?
const [fileList, setFileList] = useState([...initFiles])
useEffect(() => {
if (fileList.length === 0) {
setFileList([...initFiles])
}
}, [initFiles])
这样仍然会报同样的死循环谬误,为什么?欢送大家留言探讨。
发表回复