一、谬误复现

开发环境报如下谬误。

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

二、谬误排查

  1. 通过正文代码的形式,发现出问题的中央,是Assets组件中援用的FileUpload出了问题。正好最近也批改过FileUpload组件。
  2. 通过sourcetree比照git记录,看FileUpload组件被批改了什么?如下图。
  3. 再比照谬误提醒中的形容,其中componentWillUpdate or componentDidUpdate,揣测就是指新增的useEffect代码片断。
  4. 将上述useEffect代码片断正文掉,果然谬误隐没。

三、起因剖析

useEffect的个性表明,只有initFiles产生了扭转,46-48行代码就会执行。
既然上述useEffect代码片断事实上造成了死循环,就还阐明了一点:

  • setFileList(initFiles)扭转了initFiles,才使得useEffect中的函数再次被调用。

那么,initFiles到底是经验了怎么的变动,才使得调用可能周而复始地产生呢?

输入fileListinitFiles

console.log(fileList === initFiles)

能够发现,只有第一次render时输入true,后续全副是false

  • 第一次输入true,表明useState的入参为array时,只是简略的赋值关系,fileListinitFiles指定了同一个内存地址。
  • 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])

这样仍然会报同样的死循环谬误,为什么?欢送大家留言探讨。