You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 22:17:18