自研React组件库用Emotion遇无效Hook调用及Vite环境报错
React组件库构建问题:Hook调用警告与Vite环境错误
背景信息
自研React组件库采用Emotion作为样式方案,已确保项目及组件库的react、react-dom版本均为18.3.1,核心依赖版本如下:
{ "@emotion/css": "^11.13.5", "@emotion/react": "^11.14.0", "react": "^18.3.1", "react-dom": "^18.3.1" }
问题1:Parcel构建时的Hook调用警告
使用Parcel构建测试页面,浏览器控制台抛出警告:
Button.tsx:41 Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
- React与渲染器(如React DOM)版本不匹配
- 违反Hooks规则
- 应用中存在多份React副本
相关代码
Button组件(第41行调用useContext):
import { css } from "@emotion/css"; // [...] import { ThemeContext } from "../theme"; // [...] export function Button(options: ButtonOptions) { // 第41行 const theme = useContext(ThemeContext); const newStyle: React.CSSProperties = {}; // 省略样式赋值逻辑 // [...] let className: string = ""; const padding = "0.7rem 1rem"; switch (options.variant ?? "secondary") { case "secondary": { className = css ` /* CSS样式 */ `; break; } case "primary": { className = css ` /* CSS样式 */ `; break; } case "danger": { className = css ` /* CSS样式 */ `; break; } case "outline": { const dark = Color(theme.colors.background).isDark(); const color = dark ? "#fff" : "#000"; const hoverBg = dark ? "rgba(255, 255, 255, 0.4)" : "rgba(0, 0, 0, 0.4)"; const pressedCharColor = dark ? "#000" : "#fff"; className = css ` /* CSS样式 */ `; break; } case "outline-primary": { const dark = Color(theme.colors.background).isDark(); const color = dark ? "#fff" : "#000"; const bg = dark ? "rgba(255, 255, 255, 0.4)" : "rgba(0, 0, 0, 0.4)"; const hoverBg = dark ? "rgba(255, 255, 255, 0.6)" : "rgba(0, 0, 0, 0.6)"; const pressedCharColor = dark ? "#000" : "#fff"; className = css ` /* CSS样式 */ `; break; } } return <button className={className} style={newStyle} type={options.type ?? "button"} disabled={options.disabled ?? false} autoFocus={options.autoFocus ?? false}>{options.children}</button>; }
ThemeContext定义:
// [...] export const ThemeContext: React.Context<Theme> = createContext(lightTheme);
排查与解决
尽管你确认了版本一致,但本地引用组件库(@hydroper/metrocomponents": "file:..")大概率导致项目和组件库加载了两份独立的React副本,这是Hook警告的核心诱因。
解决步骤:
- 统一React依赖路径:
- 在组件库的
package.json中将react、react-dom设为peerDependencies,避免重复安装; - 或使用
npm link将组件库的React实例链接到项目的React包,确保两者共享同一实例。
- 在组件库的
- 确认组件调用方式:确保Button是以React组件形式使用(即
<Button />),而非普通函数调用(Button()),后者会直接触发Hook调用错误。
问题2:Vite构建时的process未定义错误
切换到Vite构建后,控制台抛出错误:
assert.js?v=00bb3fe6:1843 Uncaught ReferenceError: process is not defined
项目配置(package.json)
{ "name": "demo", "type": "module", "devDependencies": { "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "^4.3.4", "typescript": "~5.7.2", "vite": "^6.1.0" }, "scripts": { "dev": "vite", "build": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview" }, "dependencies": { "@emotion/css": "^11.13.5", "@emotion/react": "^11.14.0", "@fontsource/open-sans": "^5.1.1", "@hydroper/metrocomponents": "file:..", "react": "^18.3.1", "react-dom": "^18.3.1" } }
排查与解决
Vite默认不会在浏览器环境注入process对象,而@emotion/css的部分内部逻辑依赖了process.env,因此触发报错。
解决步骤:
- 在Vite中模拟
process变量:
在vite.config.ts中添加define配置,注入必要的process变量:import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], define: { 'process.env': {}, // 若需要特定变量,比如NODE_ENV,可设置为: // 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) } }); - 切换到Emotion的JSX语法:
改用@emotion/react的JSX pragma替代@emotion/css,这种方式更贴合React生态,也能避免对process的依赖。修改组件代码:/** @jsxImportSource @emotion/react */ import { css } from '@emotion/react'; // 组件内样式定义改为: const buttonStyle = css` /* 样式内容 */ `; - 升级Emotion版本:检查是否有更新的Emotion版本修复了
process依赖问题,尝试升级@emotion/css和@emotion/react到最新稳定版。
内容的提问来源于stack exchange,提问作者anon
相关产品推荐
相关产品推荐

