TypeScript/ReactJS 的 useContext 的正确类型是什么?

What are the right types for a useContext with TypeScript/ReactJS?

我正在重构 TypeScript 我关注的 ReactJS 中的 tutorial。在大多数情况下,本教程教您如何重构大部分代码作为奖励 material。然而,登录部分仅在 ReactJS/JavaScript 中完成,我试图重构我的代码作为挑战自己的一种方式。我坚持的部分是 createContext,因为我无法理解所需的类型。

原JS代码

JS代码-Context/Provider

import React, { useState, createContext } from "react";


export const Context = createContext();

const UserProvider = ({ children }) => {
    const [state, setState] = useState(undefined);

    return (
        <Context.Provider value={[state, setState]}>{children}</Context.Provider>
    )
};

export default UserProvider;

在登录组件上它被调用 const [_user, setUser] = useContext(Context);

我的尝试

我试过的

我尝试应用以下解决方案,但我并没有很好地掌握这些概念以成功应用它们:

TS代码-Context/Provider

import React, { useState, createContext } from "react";
import { IUser, UserContextType } from "./@types/context";


export const Context = createContext<UserContextType | undefined>(undefined);

const UserProvider: React.FC<React.ReactNode> = ({ children }) => {
    const [state, setState] = useState<IUser>();

    return (
        <Context.Provider value={{state, setState}}>{children}</Context.Provider>
    )
};

export default UserProvider;

TS 代码 - 类型

export interface IUser {
    user: string;
    password: string;
};

export type UserContextType = {
    state: IUser;
    setState: (newSession: IUser) => void;
};

错误

<Context.Provider value={{state, setState}}>{children}</Context.Provider>

Type 'IUser | undefined' is not assignable to type 'IUser'. Type 'undefined' is not assignable to type 'IUser'.ts(2322)

const [_user, setUser] = useContext(Context);

Type 'UserContextType | undefined' is not an array type.ts(2461)

这是 Typescript 上的上下文示例

AuthContext.tsx:

import { createContext, ReactNode, useContext, useState } from "react";
import { AuthContextData } from "../models/AuthContextData.model";

const AuthContext = createContext<AuthContextData>({} as AuthContextData);

export const AuthProvider = ({ children }: { children: ReactNode }) => {
  const [user, setUser] = useState<object | null>(null);
  const [loading, setLoading] = useState<boolean>(true);

  async function signIn(): Promise<void> {
   console.log('sign in')
  }

  async function signOut(): Promise<void> {
    console.log('sign out')
  }

  return (
    <AuthContext.Provider
      value={{ signed: !!user, user, signIn, signOut, loading }}
    >
      {children}
    </AuthContext.Provider>
  );
};

export function useAuth() {
  const context = useContext(AuthContext);
  return context;
}

AuthContextData.ts:

export interface AuthContextData {
  signed: boolean;
  user: object | null;
  signIn(): Promise<void>;
  signOut(): Promise<void>;
  loading: boolean;
}

首先,您像这样声明上下文类型:

export const Context = createContext<UserContextType | undefined>(undefined);

这意味着上下文的 value 要么是具有 UserContextType 所有属性的对象,要么是 undefined.

然后这里:

const [state, setState] = useState<IUser>();

您创建了 IUser 类型的状态。但是因为你没有提供默认值,所以默认值为undefined。这已添加到州的类型中。所以 state 的类型是 IUser | undefined.

然后我们到了这里:

value={{state, setState}}

stateIUser | undefined,但上下文类型需要一个 IUser[] 作为 属性。所以 IUserundefined 都不是那里的有效类型。


首先,您需要确保如果您传入的是对象,则该对象的类型是正确的。这意味着它具有所有必需的属性。否则,传入未定义的。你可以用三元表达式来做到这一点:

value={state ? {state, setState} : undefined}

现在,如果状态有值,则创建上下文值并传递给它。否则上下文值为 undefined.


但是现在你得到这个错误:

    Types of property 'state' are incompatible.
      Type 'IUser' is missing the following properties from type 'IUser[]': length, pop, push, concat, and 26 more.(2322)

这是因为您试图将单个用户 IUser 分配给一组用户 IUser[]

您的意思似乎是让内容仅容纳一个用户,因此您可能希望将上下文类型更改为:

export type UserContextType = {
    state: IUser;
    setState: (newSession: IUser) => void;
};

有效,see playground

或者你需要传入一个用户数组:

const UserProvider: React.FC<React.ReactNode> = ({ children }) => {
    const [state, setState] = useState<IUser[]>([]);

    return (
        <Context.Provider value={{state, setState}}>{children}</Context.Provider>
    )
};

这也有效,see playground


您还可以将上下文类型更改为:

export type UserContextType = {
    state?: IUser;
    setState: (newSession: IUser) => void;
};

这使得 state 可选,然后:

const UserProvider: React.FC<React.ReactNode> = ({ children }) => {
    const [state, setState] = useState<IUser>();

    return (
        <Context.Provider value={{state, setState}}>{children}</Context.Provider>
    )
};

应该可以正常工作,see playground