React,没用useState,没用useEffect,出现死循环,问题诡异

问题遇到的现象和发生背景

项目采用了阿里的umijs+dva+antd框架,系统通过“yarn start”运行后,可以正常启动运行,访问页面也没有问题。
但我注释掉一行代码后,然后系统就进入到死循环,直到页面崩溃。代码如下:

问题相关代码,请勿粘贴截图

路由配置如下:

  {
    name: 'account',
    path: '/account',
    icon: 'user',
    routes: [
      {
        name: 'settings',
        path: '/account/settings',
        component: './account/settings',
      }
    ]
  }

account/settings/index.jsx:

import MyContainer from "@/components/MyContainer";

import { useState } from "react";
import SettingForm from './setting_form';

const Index = () => {

  const [loading, setLoading] = useState(false);
  const [tip, setTip] = useState('正在加载数据...');

  console.log("settings.Index");
  console.log("settings.Index");
  debugger

  const setContainerLoading = (loading, tip) => {
    setLoading(loading);
    setTip(tip === undefined ? '' : tip);
  }

  return (
      <MyContainer loading={ loading } tip={ tip }>
        <SettingForm setLoading={ setContainerLoading }/>
      </MyContainer>
  );
}

export default Index;

MyContainer为一个页面容器
component/MyContainer/index.jsx:

import { PageContainer } from "@ant-design/pro-layout";
import styles from './index.less';
import { Spin } from 'antd';

const Index = ({loading, tip, children}) => {
  console.log("loading: ", loading);
  console.log("tip: ", tip);

  return (
      <PageContainer>
        <Spin spinning={ loading } tip={ tip }>
          <div className={ styles.pageContainer }>
            { children }
          </div>
        </Spin>
      </PageContainer>
  )
}

export default Index;

SettingForm是一个表单组件,我把其中的内容都删除了
account/settings/setting_form.jsx:

import { Form } from "antd";
import { useState } from "react";
import { cacheGet } from "@/utils/cache";
import { getUser } from "@/services/vipcti-adm/user";
import ProForm  from "@ant-design/pro-form";

const SettingForm = () => {
  const [form] = Form.useForm();

  const formItemLayout = {
    labelCol: {span: 4},
    wrapperCol: {span: 12, offset: 1},
  }

  return (
      <ProForm form={ form }
               { ...formItemLayout }
               layout='horizontal'
      >
      </ProForm>
  );
}

export default SettingForm;

setting_form.jsx中import的cache.js文件内容

utils/cache.js:

import storage from 'store';

import { listForTree } from '@/services/vipcti-adm/region';
import { fetchDictList, fetchDictIntList, fetchDictMap, fetchDictIntMap } from '@/services/vipcti-adm/dict';

export function cacheGet(key, dataType) {
  ...
}

在cache.js中又import了dict.js中的几个函数

import { fetchDictList, fetchDictIntList, fetchDictMap, fetchDictIntMap } from '@/services/vipcti-adm/dict';

dict.js中的内容如下:
services/vipcti-adm/dict.js:

import {request} from 'umi';

export function fetchDictList(type) {
  return request('/dict/list', {
    method: 'get',
    params: { type: type }
  });
}

export function fetchDictIntList(type) {
  // return request('/dict/int-list', {
  //   method: 'get',
  //   params: { type: type }
  // })
}

export function fetchDictMap(type) {
  // return request('/dict/map', {
  //   method: 'get',
  //   params: { type: type }
  // })
}

export function fetchDictIntMap(type) {
  // return request('/dict/int-map', {
  //   method: 'get',
  //   params: { type: type }
  // })
}
运行结果及报错内容

通过执行“yarn start”运行代码后,访问路由:/account/settings都正常,但是在我注释掉/account/settings/index.jsx中下面几行或一行代码并保存后:

  //console.log("settings.Index");
  console.log("settings.Index");
  debugger

页面就进入死循环,打开浏览器的console,可以发现,打印"setting.Index"后,如果有debuger,就停留到debgger那一行,点击“继续运行”按钮后,一直重复以上动作。如果注释掉debugger这一行,则console中一直打印“setting.Index",直至页面崩溃。

我的解答思路和尝试过的方法

为了查找问题,我对react源代码进行了debug,它进入了以下代码堆栈循环:

settings.Index
Component
renderWithHooks
mountIndeteminateComponent
beginWork
beginWork$1
stopProfilerTimerIfRunningAndRecordDelta
performUnitOfWork
workLoopSync

// workLoopSync是一个重复渲染组件及其子组件的过程,只要workInProcess !== null就会一直循环,而正是因为这种情况,
// 导致了代码一直在这个死循环中出不来。

function workLoopSync() {
    while(workInProcess !== null) {
        performUnitOfWork(workInProcess);
    }
}

// 在mountIndeterminateComponent函数中,调用完renderWithHooks,会执行:
reconcileChildren(null, workInProgress, value, renderLanes);
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderLanes);
function reconcileChildFibers(returnFiber, currentFirstChild, newChild, lanes);
return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes));
var _created4 = createFiberFromElement(element, returnFiber.mode, lanes);
createFiberFromTypeAndProps(type, key, pendingProps, owner, mode, lanes)
resolvedType = resolveFunctionForHotReloading(resolvedType);

正常情况下,resolvedType应该获取到子组件的类型,这样才能递归渲染下去,但实际情况是获取到了本身的组件类型,导致一直循环。
resolveFunctionForHotReloading的代码如下:

function resolveFunctionForHotReloading(type) {
  {
    if (resolveFamily === null) {
      // Hot reloading is disabled.
      return type;
    }

    var family = resolveFamily(type);

    if (family === undefined) {
      return type;
    } // Use the latest known implementation.


    return family.current;
  }
}

// 这里执行了:
var family = resolveFamily(type);

// resolveFamily代码如下:
function resolveFamily(type) {
  // Only check updated types to keep lookups fast.
  return updatedFamiliesByType.get(type);
} // If we didn't care about IE11, we could use new Map/Set(iterable).

// 这里的updatedFamiliesByType是一个Map
var updatedFamiliesByType = new PossiblyWeakMap();

debugger到这里,已经无法再继续了,那肯定是生成这个Map时,其保存的组件类型就有问题了。虽然知道在代码层面的问题,但却不知道造成死循环的根本原因, 也无法解决问题。

既然debug无法解决问题,则从另外一方面出发,通过删除掉具体的功能代码,只保留最简单的代码来尝试,看看是否还会出现死循环。上面列出的代码,即是删除功能代码后保留的最简单代码。但还是会出现死循环。通过不断尝试 注释/运行代码,发现问题代码为dict.js中的四个函数:

export function fetchDictList(type) {
  return request('/dict/list', {
    method: 'get',
    params: { type: type }
  });
}

export function fetchDictIntList(type) {
  // return request('/dict/int-list', {
  //   method: 'get',
  //   params: { type: type }
  // })
}

export function fetchDictMap(type) {
  // return request('/dict/map', {
  //   method: 'get',
  //   params: { type: type }
  // })
}

export function fetchDictIntMap(type) {
  // return request('/dict/int-map', {
  //   method: 'get',
  //   params: { type: type }
  // })
}

虽然程序中并没有调用这几个函数,只是import了一下,但也有问题,只要注释掉这几个函数,运行后,再注释掉一两行代码,就不会进入死循环了。或者保留函数,只注释掉函数中的‘return request” 代码,保留一个空函数,也不会有问题。

这个return request代码,正常来看也没啥特别,程序中也并没有调用,最诡异的是,一开始运行是没有问题的,只有在运行过程中,注释掉一两行无关紧要的代码并保存后,才会进入死循环。我百思不得其解,由于本人也是初学React,犯了什么基础性的错误也是有可能的,不知道哪位是否遇到类似问题,或者能帮忙指出问题所在,本人不胜感激!

最后发现是umijs框架的一个BUG,一个叫MSFU的配置,关掉,世界就清爽了。。。唉