React TypeScript全局变量未定义报错,求全局变量配置方案
你的问题核心在于两个点:一是通过script标签注入的全局变量在组件渲染时还未初始化,二是TypeScript无法识别这个未声明的全局变量,导致报错。下面给你几种规范且可行的方案,从简单到复杂场景分别说明:
方案一:直接挂载到window对象(简单全局变量场景)
这是React+TS中实现全局变量最常用的方式,既满足跨文件访问,又能获得TypeScript的类型支持:
1. 声明全局类型
在项目的src目录下创建一个globals.d.ts文件(TypeScript会自动识别这类声明文件),用来扩展window对象的类型:
interface Window { // 定义你的全局变量类型,这里和你要的data结构一致 data: { name: string; }; }
2. 在组件中初始化并访问全局变量
修改你的App.tsx,不需要创建script标签,直接在useEffect中给window.data赋值:
import React from 'react'; export default function App() { const initializeData = () => { // 直接挂载到window对象上 window.data = { name: "" }; }; React.useEffect(() => { initializeData(); // 这里可以安全访问全局变量 console.log(window.data); }, []); return ( <div className="App"> {/* 注意:组件第一次渲染时window.data还未初始化,所以要加判断避免报错 */} {window.data && console.log(window.data)} <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> </div> ); }
跨文件访问
其他组件中直接通过window.data访问即可,TypeScript会自动识别它的类型,不会报错。
方案二:使用React Context API(复杂跨组件共享场景)
如果你的全局变量需要被多个组件修改、订阅状态变化,推荐使用React内置的Context API,这是更符合React设计理念的状态管理方式:
1. 创建全局Context
新建DataContext.tsx文件:
import { createContext, useContext, useState, useEffect, ReactNode } from 'react'; // 定义Context的类型 interface DataContextType { data: { name: string }; setData: (newData: { name: string }) => void; } // 创建Context,初始值设为undefined const DataContext = createContext<DataContextType | undefined>(undefined); // 定义Provider组件,用来包裹需要共享数据的组件 export function DataProvider({ children }: { children: ReactNode }) { // 用useState管理全局状态 const [data, setData] = useState<{ name: string }>({ name: "" }); // 这里可以做初始化逻辑,比如从接口拉取数据 useEffect(() => { // 模拟异步初始化 setTimeout(() => { setData({ name: "Initial Global Data" }); }, 1000); }, []); return ( <DataContext.Provider value={{ data, setData }}> {children} </DataContext.Provider> ); } // 自定义Hook,简化Context的访问 export function useData() { const context = useContext(DataContext); if (!context) { throw new Error('useData must be used within a DataProvider'); } return context; }
2. 在应用中使用Context
修改App.tsx,用DataProvider包裹整个应用(或者需要共享数据的部分):
import React from 'react'; import { DataProvider, useData } from './DataContext'; // 子组件示例,任意子组件都可以通过useData访问全局数据 function AppContent() { const { data } = useData(); console.log(data); return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> </div> ); } export default function App() { return ( <DataProvider> <AppContent /> </DataProvider> ); }
这种方式的优势在于:类型安全、状态变化可追踪、不需要依赖全局window,适合中大型项目的全局状态管理。
方案三:保留script注入方式(不推荐,但可兼容)
如果你一定要用script标签注入全局变量的方式,需要解决两个问题:TypeScript的类型声明和渲染时序问题:
1. 声明全局变量类型
在globals.d.ts中添加:
// 声明data为全局变量 declare let data: { name: string };
2. 修改组件逻辑,避免时序问题
组件第一次渲染时,useEffect还未执行,script标签还没注入,所以data会未定义。需要加判断或者延迟访问:
import React from 'react'; export default function App() { const initializeData = () => { const dataScript = document.createElement("script"); dataScript.type = "text/javascript"; dataScript.textContent = ` let data = { 'name': "" };`; document.head.appendChild(dataScript); }; React.useEffect(() => { initializeData(); // 这里在useEffect中,script已经注入,可以访问data console.log(data); }, []); return ( <div className="App"> {/* 第一次渲染时data不存在,所以要判断 */} {typeof data !== 'undefined' && console.log(data)} <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> </div> ); }
但这种方式不推荐,因为依赖DOM的执行顺序,容易出现不可预期的问题,而且类型支持也需要额外声明,不如前两种方案可靠。
总结
- 简单的全局变量共享:优先选择方案一(挂载window+类型声明),实现成本低,满足跨文件访问需求。
- 需要多组件交互、状态变化的场景:优先选择方案二(Context API),符合React的状态管理规范。
- 尽量避免使用
script注入全局变量的方式,容易引发时序和类型问题。
内容的提问来源于stack exchange,提问作者Undefined

