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}}
state
是 IUser | undefined
,但上下文类型需要一个 IUser[]
作为 属性。所以 IUser
或 undefined
都不是那里的有效类型。
首先,您需要确保如果您传入的是对象,则该对象的类型是正确的。这意味着它具有所有必需的属性。否则,传入未定义的。你可以用三元表达式来做到这一点:
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;
};
或者你需要传入一个用户数组:
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
我正在重构 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}}
state
是 IUser | undefined
,但上下文类型需要一个 IUser[]
作为 属性。所以 IUser
或 undefined
都不是那里的有效类型。
首先,您需要确保如果您传入的是对象,则该对象的类型是正确的。这意味着它具有所有必需的属性。否则,传入未定义的。你可以用三元表达式来做到这一点:
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;
};
或者你需要传入一个用户数组:
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