Next13 App目录动态组件加载异常问题求助
我想创建一个组件名称与Props均来自API的动态组件,但运行时出现错误。
我的代码如下:
import React, { FC } from "react"; import dynamic from "next/dynamic"; const DynamicComponent = (componentName: string) => dynamic(() => import(`./C${componentName}`)); const CCustomComponent: FC<{ field: any }> = ({ field }) => { return React.createElement(DynamicComponent(field.blockType), { // @ts-ignore field: field, }); }; export default CCustomComponent;
报错信息:
Unhandled Runtime Error Error: Cannot read properties of null (reading 'useContext')
调用栈指向/node_modules/next/dist/compiled/react/cjs/react.development.js中的Object.useContext、useLoadableModule,以及node_modules/next/dist/shared/lib/loadable.js中的LoadableComponent。我没在组件里用任何Hook,问题集中在动态导入逻辑,而且是偶发:部署后的页面能正常运行,但开发和构建环境会失效。
这个问题的核心是在组件渲染过程中动态创建了dynamic组件,Next.js的dynamic方法必须在组件外部(顶层作用域)声明,不能在渲染周期内动态生成——哪怕你没显式用Hook,dynamic内部也依赖了React Context,这种写法会破坏React的Hook规则。
修复方案1:提前预定义组件映射
把所有可能从API返回的组件名称和对应动态导入做映射,放在组件外部:
import React, { FC } from "react"; import dynamic from "next/dynamic"; // 预定义所有可能的组件映射 const componentMap = { ComponentA: dynamic(() => import("./CComponentA")), ComponentB: dynamic(() => import("./CComponentB")), // 补充其他可能的组件... }; const CCustomComponent: FC<{ field: any }> = ({ field }) => { const TargetComponent = componentMap[field.blockType]; if (!TargetComponent) { // 处理组件不存在的情况,返回占位符 return <div>未知组件</div>; } return <TargetComponent field={field} />; }; export default CCustomComponent;
修复方案2:用缓存避免重复创建动态组件
如果组件数量太多没法逐个枚举,用缓存对象存储已创建的动态组件,避免每次渲染都重新调用dynamic:
import React, { FC, useMemo } from "react"; import dynamic from "next/dynamic"; // 缓存已创建的动态组件 const componentCache = new Map<string, React.ComponentType>(); const getDynamicComponent = (componentName: string) => { if (!componentCache.has(componentName)) { const Component = dynamic(() => import(`./C${componentName}`)); componentCache.set(componentName, Component); } return componentCache.get(componentName)!; }; const CCustomComponent: FC<{ field: any }> = ({ field }) => { const TargetComponent = useMemo(() => getDynamicComponent(field.blockType), [field.blockType]); return <TargetComponent field={field} />; }; export default CCustomComponent;
错误原因说明
Next.js的dynamic内部会用React的useContext Hook处理加载状态和SSR逻辑,而React要求Hook必须在组件顶层作用域调用。之前的写法每次渲染都会重新执行dynamic,相当于在渲染过程中动态执行Hook,违反了规则。部署环境可能因缓存或打包优化偶然生效,但开发/构建环境的严格检查会触发错误。
内容的提问来源于stack exchange,提问作者Wiktor Kujawa

