开发者问题收集

在 Typescript 中创建 context + Reducer 时出错

2021-09-12
1586

在这里我想创建一个 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>
    )
}

这是我的 Reducer:

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.

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

2个回答

您正在 创建 的上下文与您 使用的 的上下文不匹配。

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

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

使用 一个上下文,其 是具有属性 statedispatch 的对象:

<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 游乐场链接


我在你的 Reducer 上收到一个不相关的错误,TS 7030“并非所有代码路径都返回值。”Reducer 应该始终在 switch 中有一个 default 案例,以避免返回 undefined 状态。

default: return state;
Linda Paiste
2021-09-12

问题是您将状态和调度作为值传递给提供程序,但是当您定义上下文时,您只将 Istate 作为其类型传递。您的 Createcontext 应该同时包含状态和调度作为类型。这也会改变初始值。

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

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

Sanish Joseph
2021-09-12