Day6的一个tips:Electron在Monorepo React 渲染错误排查:从 Hook 崩溃到根治

简介: 记录 Monorepo 中 Web 与 Electron 端 React "Invalid hook call" 的完整排查过程,涵盖版本错配、实例混用、CSP 拦截等根因分析与修复方案。

渲染层报错复盘(Web + Electron)

1. 背景

在完成 Zustand Store 迁移后,webdesktop(electron renderer) 先后出现 React 渲染错误,主要表现为:

  • Invalid hook call
  • Cannot read properties of null (reading 'useRef' / 'useState')
  • Incompatible React versions

这些错误会导致页面无法渲染,属于高优先级阻塞问题。


2. 问题现象

2.1 Web 端

关键报错:

  • Invalid hook call. Hooks can only be called inside of the body of a function component.
  • 触发点:<BrowserRouter>main.tsx:9
  • 连带报错:Cannot read properties of null (reading 'useRef')

2.2 Web 端二次报错(修复过程中的中间态)

关键报错:

  • Incompatible React versions
  • react: 19.1.0
  • react-dom: 19.2.5

说明渲染时拿到的 reactreact-dom 不是同一版本。

2.3 Electron 渲染进程

关键报错:

  • Invalid hook call
  • Cannot read properties of null (reading 'useState')
  • 触发组件:Versions.tsx

3. 根因分析

根因 A:Monorepo 下 React 实例混用

在 workspace 场景下,不同 app 的 reactreact-dom 解析路径可能不同,导致:

  • 业务代码使用了一份 react
  • 渲染器(react-dom / router)使用了另一份 react

从而触发 Invalid hook call

根因 B:一次错误修复带来的版本错配

为修复 Web 端 Invalid hook call,曾尝试在 Vite 中把 react alias 到 workspace 根。随后发现根目录实际版本为:

  • root react = 19.1.0
  • root react-dom = 19.2.5

这会直接触发 Incompatible React versions

根因 C:Electron 渲染端依赖归类导致运行时解析不稳定

apps/desktop 初始将 reactreact-dom 放在 devDependencies,配合 monorepo hoist 后,渲染进程更容易解析到非预期位置的包。


4. 排查路径(实际执行)

  1. 定位异常入口:

    • apps/web/src/main.tsx<BrowserRouter>
    • apps/desktop/src/renderer/src/components/Versions.tsx
  2. 检查版本与依赖树:

    • pnpm --filter @aitodos/web why react
    • pnpm --filter @aitodos/store why react
    • pnpm --filter desktop why react
  3. 检查运行时解析路径(关键):

    • require.resolve('react')
    • require.resolve('react-dom')
    • 分别在 apps/webapps/adminapps/desktop 目录执行
  4. 检查本地安装版本:

    • node_modules/react/package.json
    • node_modules/react-dom/package.json

5. 修复动作(按时间顺序)

5.1 Web/Admin 首轮修复

文件:

  • apps/web/vite.config.ts
  • apps/admin/vite.config.ts

动作:

  • 增加 resolve.dedupe: ['react', 'react-dom']

作用:

  • 降低同一应用加载多份 React 的风险。

5.2 修复过程中的错误动作(已回滚)

动作:

  • 在 Web/Admin Vite 里把 react/react-dom alias 到 workspace 根

结果:

  • 命中了 root react=19.1.0react-dom=19.2.5 的版本错配
  • 出现 Incompatible React versions

处理:

  • 回滚该 alias,仅保留 dedupe

5.3 Electron Renderer 修复

文件:

  • apps/desktop/electron.vite.config.ts
  • apps/desktop/package.json

动作 1(解析一致化):

  • renderer.resolve 中保留 dedupe: ['react', 'react-dom']
  • 显式增加:react: resolve('node_modules/react')

动作 2(依赖归类修正):

  • reactreact-domdevDependencies 移到 dependencies

目的:

  • 保证 Electron 渲染进程运行时拿到同一套 React 运行时依赖。

5.4 依赖重装与重启

执行:

pnpm install --filter desktop
pnpm --filter desktop dev

结果:

  • 安装成功(exit code 0)
  • Electron renderer dev server 启动成功

6. 最终状态

  • Web 端渲染恢复正常
  • Electron 渲染进程可启动并进入页面
  • Invalid hook call 链路已完成定向修复

7. 可复用修复模板

当再次出现 Invalid hook call 时,建议按以下顺序:

  1. 先查“版本一致性”

    • reactreact-dom 必须完全同版本
  2. 再查“解析路径一致性”

    • 比较 require.resolve('react')require.resolve('react-dom')
  3. 在 bundler 中启用去重

    • resolve.dedupe: ['react', 'react-dom']
  4. 对 Electron/运行时 app,确保 React 是运行时依赖

    • 放在 dependencies 而非仅 devDependencies
  5. 每次改完配置必须重启进程

    • 停掉旧的 dev 进程
    • 重新启动并强刷页面

8. 备注

  • apps/desktoppnpm.onlyBuiltDependencies 配置警告属于配置位置提示,不是本次渲染错误根因。
  • 若后续仍偶发异常,优先加运行时日志打印 React 解析路径和版本再定位。

9. 补充:Electron 复用 Web 后样式丢失与 CSP 报错

9.1 问题现象 A:页面“有结构、无样式”

现象:

  • Electron 页面能渲染文本和结构,但 Tailwind 视觉样式几乎全部失效。
  • 页面看起来像纯 HTML 默认样式。

关联报错(阶段性):

  • [plugin:vite:import-analysis] Failed to resolve import "@web/index.css"

根因:

  1. main.tsx 直接从 @web/index.css 引入时,CSS alias 在 Electron + Vite 场景下解析不稳定。
  2. apps/desktop 原模板样式文件未显式扫描 apps/web/src,导致 Tailwind 未生成 Web 页面实际使用的 utility class。

修复:

  • apps/desktop/src/renderer/src/main.tsx
    • 使用 desktop 本地样式入口:import './assets/main.css'
  • apps/desktop/src/renderer/src/assets/main.css
    • 保留 @import "tailwindcss";
    • 增加扫描源:
      • @source "../../../../../web/src/**/*.{ts,tsx}";
      • @source "../**/*.{ts,tsx}";
    • 移除 Electron 模板页遗留样式,保留通用基础规则(html/body/#root)。

验证:

  • pnpm --filter desktop build 通过。
  • 构建后的 renderer CSS 体积明显增大(说明 Tailwind utility 已成功生成)。

9.2 问题现象 B:CSP 拒绝 unsafe-eval

关键报错:

  • Uncaught EvalError: Evaluating a string as JavaScript violates CSP ... 'unsafe-eval'
  • 触发点:packages/shared/src/storage.ts

根因:

  • 为了条件加载 Taro/RN 存储实现,代码中使用了 new Function(...) 进行动态导入;
  • Electron 渲染进程默认 CSP 不允许 unsafe-eval,因此直接报错。

修复:

  • packages/shared/src/storage.ts
    • new Function(...) 改为 CSP 安全写法:
      • import(/* @vite-ignore */ modulePath)

验证:

  • pnpm --filter @aitodos/shared type-check 通过。
  • pnpm --filter desktop typecheck 通过。
  • Electron 运行时不再出现 unsafe-eval 相关异常。

9.3 结论

  • 复用 Web 到 Electron 时,样式链路要以 desktop 为入口统一管理;
  • Tailwind v4 需要在 desktop 的 CSS 入口显式声明 @source 扫描 Web 源码;
  • 渲染层共享代码必须避免 eval/new Function,否则容易被 CSP 拦截。
相关文章
|
27天前
|
机器学习/深度学习 搜索推荐 算法
拆解推荐系统:候选生成、过滤、排序、多样性的分层设计
推荐系统是端到端流水线,非单一算法:涵盖候选生成、过滤、特征工程、多目标排序、多样性调控与反馈闭环。强调关注点分离,以保障质量、速度与行为可控。动手前须明确定义Item、用户行为及成功指标。
267 12
拆解推荐系统:候选生成、过滤、排序、多样性的分层设计
|
1月前
|
人工智能 机器人 网络安全
使用 Lume 在 macOS 虚拟机中隔离运行 OpenClaw/Moltbot:完整部署指南
OpenClaw(龙虾)是开源本地AI智能体,可7×24小时在你设备上自主执行任务。支持微信/飞书/Telegram等50+渠道,具备持久记忆、网页浏览、文件操作与自我扩展能力,数据全留本地,隐私可控。(239字)
379 7
使用 Lume 在 macOS 虚拟机中隔离运行 OpenClaw/Moltbot:完整部署指南
|
29天前
|
存储 弹性计算 监控
阿里云云服务器ECS实例介绍及选择建议
阿里云ECS是安全可靠、弹性伸缩的云服务器服务,提供通用型、计算型、内存型等8类实例,适配Web、数据库、AI、大数据等场景。支持免费试用,兼顾性能与成本,助力企业降本增效。
344 13
|
2月前
|
Java 数据库 数据格式
【Spring注解】Spring生态常见注解——面试高频考点总结
本文系统梳理Spring生态高频面试注解考点,直击本质——注解只是表象,核心考察IOC容器、Bean生命周期、MVC流程、AOP代理、自动配置与事务机制等底层原理。涵盖@Component/@Configuration、@Autowired/@Resource、@RestController、@Transactional、@SpringBootApplication、@RequestBody等七大类注解的辨析、原理、坑点 及 TOP10 必背题。
337 12
|
28天前
|
存储 人工智能 JavaScript
Prompt、Context、Harness:AI Agent 工程的三层架构解析
2023年重“Prompt”(如何说),2025年重“Context”(看到什么),2026年跃升至“Harness”(系统级约束与验证)。三者非替代而是分层:Prompt优化表达,Context管理信息环境,Harness构建可信执行系统——模型是马,Harness才是缰绳、马鞍与路。
517 10
Prompt、Context、Harness:AI Agent 工程的三层架构解析
|
2月前
|
关系型数据库 MySQL 开发者
5个让MySQL查询飞起来的实用技巧
5个让MySQL查询飞起来的实用技巧
|
2月前
|
消息中间件 SQL 存储
深入剖析RocketMQ1-基础使用
本文介绍了RocketMQ消息队列的核心概念、集群部署及消息发送示例。首先阐述了消息队列的作用包括应用解耦、流量削峰和数据分发,并对比了不同MQ产品的优缺点。重点讲解了RocketMQ的集群架构模式,详细演示了双主双从集群(2m-2s同步双写)的搭建过程,包括环境配置、Broker参数设置和集群启动。最后通过代码示例展示了基本消息发送接收、顺序消息、延迟消息、批量消息、消息过滤和事务消息的实现方式,为开发者提供了RocketMQ的实用指南。
|
2天前
|
人工智能 安全
深度测评:2026年中国医院精子SDF检测产品实测推荐
本文基于WHO标准与临床实测,从准确性、质控、报告维度及合规性四方面对比目前国内生殖中心常见的精子DFI检测四大品牌。结果显示:星博生物凭借AI纠错、双平台技术、全流程质控及首批二类证,综合表现最优,是精准评估男性生育力的首选。
|
3天前
|
人工智能 运维 架构师
我在 AIP 智能体平台踩过的坑,都在这篇企业 AI 落地经验里了
软件架构师罗小东分享企业AI落地实战经验:聚焦AIP智能体平台建设中的真实坑点与解法——涵盖智能体全生命周期管理、多源知识库语义检索、MCP工具集成及多模型中立架构设计,强调“解决问题”而非堆砌功能。(239字)

热门文章

最新文章