javascript - Hva er de riktige typene for en useContext med TypeScript/ReactJS?
Jeg refaktorerer en tutorial i ReactJS til TypeScript som jeg følger. For det meste lærer tutorialen deg hvordan du refaktorerer mesteparten av koden som bonusmateriale. Men påloggingsdelen er bare laget i ReactJS/JavaScript, og jeg prøvde å refaktorere koden min som en måte å utfordre meg selv på. Delen jeg sitter fast på, er createContext siden jeg ikke klarer å forstå hvilke typer som trengs.
Original JS-kode
JS-kode - Kontekst/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;
På Login-komponenten blir det kalt med const [_user, setUser]=useContext(Context);
Mitt forsøk
Hva jeg har prøvd
Jeg prøvde å bruke følgende løsninger, og jeg forstår egentlig ikke konseptene godt nok til å bruke dem med suksess:
- Kan ikke deklarere React-kontekst i TypeScript? [duplikat]
- Kan ikke finne navnerommet 'ctx'-feil når du oppretter Context med react - typescript
- Hvordan bruke React Context med TypeScript
- React Context + TypeScript — Den enkle måten
TS-kode - Kontekst/Leverandør
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-kode - typer
export interface IUser {
user: string;
password: string;
};
export type UserContextType = {
state: IUser;
setState: (newSession: IUser) => void;
};
Feil
<Context.Provider value={{state, setState}}>{children}</Context.Provider>
Type 'IUser | undefined' kan ikke tilordnes typen 'IUser'. Type 'undefined' kan ikke tilordnes typen 'IUser'.ts(2322)
const [_user, setUser] = useContext(Context);
Type 'UserContextType | undefined' er ikke en array-type.ts(2461)
Svar #1
Først av alt deklarerer du konteksttypen slik:
export const Context = createContext<UserContextType | undefined>(undefined);
Dette betyr at value av konteksten enten er et objekt med alle egenskaper til stede fra UserContextType eller at det er undefined.
Så her:
const [state, setState] = useState<IUser>();
Du oppretter en state som har en type IUser. Men fordi du ikke angir en standardverdi, er standardverdien undefined. Dette legges til i statens type for deg. Så state er av typen IUser | undefined.
Da kommer vi hit:
value={{state, setState}}
state er IUser | undefined, men konteksttypen forventer et IUser[] for den egenskapen. Så verken IUser eller undefined er gyldige typer der.
Først må du sørge for at hvis du sender inn et objekt, så er objektet korrekt typet. Det betyr at det har alle nødvendige egenskaper til stede. Ellers, send inn undefined. Du kan gjøre dette med et ternært uttrykk:
value={state ? {state, setState} : undefined}
Hvis tilstanden nå har en verdi, blir kontekstens verdi opprettet og sendt den. Ellers er kontekstens verdi undefined.
Men nå får du denne feilen:
Types of property 'state' are incompatible.
Type 'IUser' is missing the following properties from type 'IUser[]': length, pop, push, concat, and 26 more.(2322)
Dette er fordi du prøver å tilordne en enkelt bruker IUser til et array av brukere IUser[].
Det ser ut som du mener at innholdet kun skal inneholde én bruker, så du vil antagelig endre kontekstens type til:
export type UserContextType = {
state: IUser;
setState: (newSession: IUser) => void;
};
Hvilke fungerer, se lekeplass
Eller du må sende inn et array av brukere:
const UserProvider: React.FC<React.ReactNode> = ({ children }) => {
const [state, setState] = useState<IUser[]>([]);
return (
<Context.Provider value={{state, setState}}>{children}</Context.Provider>
)
};
Som også fungerer, se lekeplass
Du kan også endre konteksttypen til:
export type UserContextType = {
state?: IUser;
setState: (newSession: IUser) => void;
};
Som gjør state valgfri, og så:
const UserProvider: React.FC<React.ReactNode> = ({ children }) => {
const [state, setState] = useState<IUser>();
return (
<Context.Provider value={{state, setState}}>{children}</Context.Provider>
)
};
Bør fungere fint, se lekeplass
Svar #2
Dette er et eksempel på en kontekst i 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;
}
