React/Vite构建UI组件库时ThemeProvider失效问题求助
Datepicker组件构建后报错:Cannot read 'white' of undefined
基于React和Vite开发UI组件库,引入Datepicker组件后执行npm run build出现错误:Cannot read property 'white' of undefined,原因是样式中访问theme.colors.white时theme为undefined,推测styled-components的<ThemeProvider>未生效,开发环境正常,仅构建后出现问题。
相关代码
Datepicker.tsx
import { useState } from "react"; import InputMask from "react-input-mask"; import { setDefaultOptions } from "date-fns"; import { ThemeProvider } from "styled-components"; import theme from "../../styles/theme"; import GlobalStyles from "../../styles/global"; import { DatepickerInputProps, ViewModeEnum, DateObject, } from "./Datepicker.types"; import { DatepickerContent } from "./elements/DatepickerContent"; import { DatepickerHelper } from "./Datepicker.helper"; import * as S from "./Datepicker.styles"; export function Datepicker({ variant = "light", isDisabled, placeholder, isRange = false, config, }: DatepickerInputProps) { const initialState = isRange ? { startDate: new Date(), endDate: null } : new Date(); const maskValue = DatepickerHelper.getMaskValue(isRange, config?.dateFormat); const [selectedDate, setSelectedDate] = useState<Date | DateObject>( initialState ); const [activeDate, setActiveDate] = useState(new Date()); const [dateValue, setDateValue] = useState(""); const [isOpen, setIsOpen] = useState(false); const [viewMode, setViewMode] = useState(ViewModeEnum.DAYS); const [error, setError] = useState(false); const locale = config?.locale || "en-US"; const language = DatepickerHelper.getLanguage(locale); const errorMessage = config?.inputStyles?.errorConfig?.errorText || "Invalid Date"; setDefaultOptions({ locale: language }); const handleOpenCalendar = () => { setViewMode(ViewModeEnum.DAYS); let parsedDate; if (isRange) { parsedDate = DatepickerHelper.stringToRangeDateOrNewDate( dateValue, config?.dateFormat ); setActiveDate(parsedDate.startDate); } else { parsedDate = DatepickerHelper.stringToDateOrNewDate( dateValue, config?.dateFormat ); setActiveDate(parsedDate); } setSelectedDate(parsedDate); setIsOpen(!isOpen); }; return ( <ThemeProvider theme={theme}> <GlobalStyles /> <S.Wrapper isDisabled={isDisabled} config={config} variant={variant} error={error} > <S.InputWrapper> <InputMask mask={maskValue} maskChar="" disabled={isDisabled} value={dateValue} onBlur={() => DatepickerHelper.checkIfDateIsValid( dateValue, isRange, setError, config?.dateFormat ) } onChange={(e: React.ChangeEvent<HTMLInputElement>) => setDateValue(e.target.value) } > {/* @ts-expect-error external lib */} {(inputProps) => ( <S.Input type="text" placeholder={placeholder} disabled={isDisabled} {...inputProps} /> )} </InputMask> <S.DatepickerWrapper onBlur={() => { setIsOpen(false); DatepickerHelper.checkIfDateIsValid( dateValue, isRange, setError, config?.dateFormat ); }} disabled={isDisabled} > <S.Icon className="fi fi-rr-calendar" onClick={() => handleOpenCalendar()} /> {isOpen && ( <DatepickerContent config={config} variant={variant} viewMode={viewMode} activeDate={activeDate} selectedDate={selectedDate} dateValue={dateValue} isRange={isRange} onChangeViewMode={setViewMode} onChangeActiveDate={setActiveDate} onChangeSelectedDate={setSelectedDate} onChangeDateValue={setDateValue} onOpen={setIsOpen} onError={setError} /> )} </S.DatepickerWrapper> </S.InputWrapper> {error && ( <S.Error> <S.ErrorIcon className="fi fi-rr-exclamation" /> <span>{errorMessage}</span> </S.Error> )} </S.Wrapper> </ThemeProvider> ); }
package.json
{ "name": "react-lib-ui", "private": false, "version": "0.0.0", "type": "module", "main": "dist/main.js", "types": "dist/main.d.ts", "files": [ "dist" ], "scripts": { "dev": "vite", "build": "tsc --p ./tsconfig-build.json && vite build", "prepublishOnly": "npm run build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, "peerDependencies": { "react": ">= 16.8.0", "react-dom": ">= 16.8.0" }, "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "@flaticon/flaticon-uicons": "^3.1.0", "date-fns": "^3.3.1", "react-input-mask": "^2.0.4", "styled-components": "^6.1.8" }, "devDependencies": { "@types/node": "^20.11.19", "@types/react": "^18.2.55", "@types/react-input-mask": "^3.0.5", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "@vitejs/plugin-react": "^4.2.1", "eslint": "^8.56.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", "glob": "^10.3.10", "react": "^18.2.0", "react-dom": "^18.2.0", "typescript": "^5.2.2", "vite": "^5.1.0", "vite-plugin-dts": "^3.7.2", "vite-plugin-lib-inject-css": "^1.3.0" }, "sideEffects": [ "**/*.css" ] }
调用组件的App.tsx
import "./App.css"; import { Datepicker } from "../"; function App() { return ( <> <Datepicker /> </> ); } export default App;
报错情况
执行npm run build后引入组件,出现Cannot read property 'white' of undefined错误,指向样式中theme.colors.white的访问,说明<ThemeProvider>未成功注入主题对象。
解决方案
1. 调整styled-components依赖位置
将styled-components从dependencies移至peerDependencies,避免组件库与项目重复安装导致版本冲突:
"peerDependencies": { "react": ">= 16.8.0", "react-dom": ">= 16.8.0", "styled-components": ">= 6.0.0" }, "dependencies": { "@flaticon/flaticon-uicons": "^3.1.0", "date-fns": "^3.3.1", "react-input-mask": "^2.0.4" },
2. 配置Vite支持styled-components
在vite.config.ts中添加babel插件支持styled-components的编译,确保主题正确注入:
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import dts from 'vite-plugin-dts'; import injectCss from 'vite-plugin-lib-inject-css'; export default defineConfig({ plugins: [ react({ babel: { plugins: ['babel-plugin-styled-components'], }, }), dts(), injectCss(), ], build: { lib: { entry: 'src/main.ts', name: 'ReactLibUI', formats: ['es', 'umd'], fileName: (format) => `main.${format}.js`, }, rollupOptions: { external: ['react', 'react-dom', 'styled-components'], output: { globals: { react: 'React', 'react-dom': 'ReactDOM', 'styled-components': 'styled', }, }, }, }, });
3. 检查主题文件导出
确保theme.ts是默认导出,避免导入时获取到undefined:
// src/styles/theme.ts export default { colors: { white: '#ffffff', // 其他颜色配置 }, // 其他主题属性 };
4. 确保子组件在ThemeProvider范围内
检查DatepickerContent内部的styled组件是否被ThemeProvider正确包裹,避免子组件脱离主题上下文。
5. 禁用Tree Shaking对主题文件的影响
在package.json的sideEffects中添加主题文件路径,防止构建时被Tree Shaking移除:
"sideEffects": [ "**/*.css", "src/styles/theme.ts" ]
执行以上步骤后重新构建组件库,问题应该可以解决。
内容的提问来源于stack exchange,提问作者Herbert Cabus
相关产品推荐
相关产品推荐

