TypeScript创建AuthContext与useReducer报错及Hook调用异常求解
问题原因及解决方案
1. 类型定义不匹配导致的TS编译报错
错误根源
- 你创建
AuthContext时指定的泛型是IState,但实际给Provider传递的value是包含state和dispatch的对象,二者类型完全不匹配,这是Provider行报错的直接原因 AuthReducer没有处理default分支,且LOGOUT操作返回的状态缺失user和token字段,导致函数可能返回undefined,不符合useReducer要求的reducer必须始终返回完整state的规则,这是useReducer行报错的直接原因initialState的user和token是字符串空值,和IState定义的string | null类型不兼容
修复后的AuthContext代码
import { createContext, ReactNode, useReducer, Dispatch } from 'react' import { AuthReducer } from './Reducer'; export interface IState { isAuthenticated: boolean user: string | null token: string | null } // reducer action 接口定义 export interface IAction { type: 'LOGIN' | 'LOGOUT' | 'REGISTER' | 'FORGOT PASSWORD' payload?: any } // 新增Context的实际类型定义 interface IAuthContextType { state: IState dispatch: Dispatch<IAction> } interface IAuthProvider { children: ReactNode } const initialState: IState = { isAuthenticated: false, user: null, token: null } // 用正确的类型创建Context,默认值可以用类型断言占位 export const AuthContext = createContext<IAuthContextType>({} as IAuthContextType); export const AuthProvider = ({ children }: IAuthProvider) => { const [state, dispatch] = useReducer(AuthReducer, initialState) return ( <AuthContext.Provider value={{ state, dispatch }}> {children} </AuthContext.Provider> ) }
修复后的Reducer代码
import { IAction, IState } from "."; export const AuthReducer = (state: IState, action: IAction): IState => { switch (action.type) { case 'LOGIN': localStorage.setItem("user", action.payload.user) localStorage.setItem("token", action.payload.token) return { ...state, isAuthenticated: true, user: action.payload.user, token: action.payload.token } case 'LOGOUT': localStorage.clear() return { ...state, isAuthenticated: false, // 补全缺失字段,符合IState类型要求 user: null, token: null } // 新增default分支,未匹配到的action直接返回当前state default: return state } }
2. 登录页Hook调用报错
错误根源
90%以上的这类报错在确认符合Hook规则的情况下,都是因为使用AuthContext的组件没有被包裹在AuthProvider的子节点中。useContext只能获取对应Provider上层提供的上下文,如果你没有在外层包裹AuthProvider,就会触发异常。其次如果IProps未定义也可能导致组件识别异常。
修复方案
- 首先在根组件(比如App.tsx或者main.tsx)中用AuthProvider包裹所有需要用到用户状态的组件,示例:
// main.tsx 示例 import React from 'react' import ReactDOM from 'react-dom/client' import { AuthProvider } from './AuthContext' import { Login } from './Login' import App from './App' ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <AuthProvider> {/* 所有子组件都可以正常使用AuthContext */} <App /> <Login /> </AuthProvider> </React.StrictMode>, )
- 补充登录页缺失的IProps定义,或者如果组件没有props直接去掉泛型即可,修复后的登录页代码:
import React, { FC, useContext } from 'react' import { AuthContext } from '../AuthContext' // 无props的话可以直接写FC,不需要加IProps泛型 export const Login: FC = () => { const { state, dispatch } = useContext(AuthContext) const login = () => { dispatch({ type: 'LOGIN', // 这里payload要按实际业务传user和token,你原来的写法不符合reducer的处理逻辑 payload: { user: 'testUser', token: 'testToken' } }) } return ( <div> <input name="username" /> <button onClick={login}>登录</button> </div> ) }
- 如果以上操作后还报错,检查package.json中的react、react-dom、@types/react、@types/react-dom版本是否一致,删除node_modules和lock文件重新安装即可。
内容的提问来源于stack exchange,提问作者frsnmk
相关产品推荐
相关产品推荐

