在 Typescript 中创建 context + reducer 时出错

Error when creating context + reducer in Typescript

这里我想创建一个AuthContext来与其他组件共享用户状态。这里我使用 TypeScript 来设置变量类型。但是在尝试解决这个问题时我遇到了很多错误。我对这个问题很困惑。

这是我的 AuthContext:

import { createContext, ReactNode, useReducer } from 'react'
import { AuthReducer } from './Reducer';

export interface IState {
    isAuthenticated: boolean
    user: string | null
    token: string | null
}

// interface for action reducer
export interface IAction {
    type: 'LOGIN' | 'LOGOUT' | 'REGISTER' | 'FORGOT PASSWORD'
    payload?: any
}

interface IAuthProvider {
    children: ReactNode
}

const initialState = {
    isAuthenticated: false,
    user: '',
    token: ''
}

export const AuthContext = createContext<IState>(initialState);


export const AuthProvider = ({ children }: IAuthProvider) => {
    const [state, dispatch] = useReducer(AuthReducer, initialState)

    return (
        <AuthContext.Provider value={{ state, dispatch }}>
            {children}
        </AuthContext.Provider>
    )
}

这是我的减速器:

import { IAction, IState } from ".";



export const AuthReducer = (state: IState, action: IAction) => {
    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,

            }
    }

}

这是我在 useReducer 行中的错误:

No overload matches this call.
  Overload 1 of 5, '(reducer: ReducerWithoutAction<any>, initializerArg: any, initializer?: undefined): [any, DispatchWithoutAction]', gave the following error.
    Argument of type '(state: IState, action: IAction) => { isAuthenticated: boolean; user: any; token: any; } | undefined' is not assignable to parameter of type 'ReducerWithoutAction<any>'.
  Overload 2 of 5, '(reducer: (state: IState, action: IAction) => { isAuthenticated: boolean; user: any; token: any; } | undefined, initialState: never, initializer?: undefined): [...]', gave the following error.
    Argument of type '{ isAuthenticated: boolean; user: string; token: string; }' is not assignable to parameter of type 'never'.ts(2769)

这是我在第 <AuthContext.Provider value={{ state, dispatch }}> 行的错误:

Type '{ state: any; dispatch: React.DispatchWithoutAction; }' is not assignable to type 'IState'.
  Object literal may only specify known properties, and 'state' does not exist in type 'IState'.ts(2322)

有谁知道解决方案或告诉您发生了什么事?谢谢

已编辑: 当我在登录页面上使用我的上下文时,我遇到了另一个问题。 这是我的 LoginPage:

import React, { FC, useContext } from 'react'
import { AuthContext } from '../AuthContext'


export const Login: FC<IProps> = () => {
    const { state, dispatch } = useContext(AuthContext)
    const login = () => {
        dispatch({
            type: 'LOGIN',
            payload: { ...state, isAuthenticated: !state.isAuthenticated }
        })
    }
    return (
        <div>
            <input name="username" />
            <button onClick={login}></button>
        </div>
    )
}

当我 运行 代码时,显示错误如下:

Error: 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:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

我已经阅读了文档,但我不认为我在那里违反了任何挂钩规则。我哪里错了抱歉我是新手。

问题是你将状态和分派作为值传递给提供者,但是当你定义上下文时,你只传递 Istate 作为它的类型。您的 Createcontext 应包括状态和分派类型。这也会改变初始值。

您提供给提供者的值的类型和您的上下文的类型应该相同。

例如:https://www.pluralsight.com/guides/using-react's-context-api-with-typescript

创建的上下文与您[=54]的上下文不匹配=]使用.

创建一个上下文,其value是一个类型为IState:[=32的状态对象=]

export const AuthContext = createContext<IState>(initialState);

使用一个上下文,其value是一个具有属性state和[=19=的对象]:

<AuthContext.Provider value={{ state, dispatch }}>

为了解决此不匹配问题,您必须以符合您的使用方式的方式定义 AuthContext上下文值需要是具有属性 statedispatch.

的对象

这里我们为上下文值定义了一个接口。 state 的默认值是 initialState,而 dispatch 的默认值记录一条错误消息。

interface AuthContextValue {
    state: IState;
    dispatch: React.Dispatch<IAction>;
}

export const AuthContext = createContext<AuthContextValue>({
    state: initialState,
    dispatch: (action) => console.error("Dispatched action outside of an AuthContext provider", action)
});

TypeScript Playground Link

这是一个更简单的版本,我们在其中定义内联上下文值类型,并且在使用默认值调用 dispatch 时不执行任何操作。

export const AuthContext = createContext<{state: IState; dispatch: React.Dispatch<IAction>}>({
    state: initialState,
    dispatch: () => {},
});

TypeScript Playground Link


我在你的减速器上收到一个不相关的错误,TS 7030“不是所有代码路径 return 一个值。” reducer 应该总是在 switch 中有一个 default 的情况,以避免 returning 一个 undefined 状态。

default: return state;