reactjs - React TS useContext useReducer-hook
Jag kan inte lista ut vad typfelet är i den här koden
import React from 'react';
interface IMenu {
items: {
title: string,
active: boolean,
label: string
}[]
}
type Action =
| { type: 'SET_ACTIVE', label: string }
const initialState: IMenu = {
items: [
{ title: 'Home', active: false, label: 'home' },
{ title: 'Customer', active: false, label: 'customer' },
{ title: 'Employee', active: false, label: 'employee' },
{ title: 'Report', active: false, label: 'report' },
]
}
const reducer = (state: IMenu = initialState, action: Action) => {
switch (action.type) {
case 'SET_ACTIVE': {
const label = action.label;
const items = state.items.map((item) => {
if (item.label === label) {
return { ...item, active: true };
}
return { ...item, active: false }
})
return { items }
}
default:
throw new Error();
}
};
export const MenuContext = React.createContext(initialState);
export const MenuConsumer = MenuContext.Consumer;
export function MenuProvider(props: any) {
const [state, dispatch] = React.useReducer(reducer, initialState)
const value = { state, dispatch };
console.log(value)
return (
<MenuContext.Provider value={value}>
{props.children}
</MenuContext.Provider>
)
}
Felet jag får är detta
/Volumes/Tarang External/react-context-typescript/src/context/index.tsx
TypeScript error in /Volumes/Tarang External/react-context-typescript/src/context/index.tsx(49,27):
Property 'items' is missing in type '{ state: { items: { active: boolean; title: string; label: string; }[]; }; dispatch: Dispatch<{ type: "SET_ACTIVE"; label: string; }>; }' but required in type 'IMenu'. TS2741
47 | console.log(value)
48 | return (
> 49 | <MenuContext.Provider value={value}>
| ^
50 | {props.children}
51 | </MenuContext.Prov
Kan någon hjälpa till att påpeka exakt vad jag gör fel? Jag är ny på TypeScript så var snäll och vägled mig om det behövs. Jag försöker skicka kontextvärdet av state och dispatch till barnkomponenten. Jag är inte säker på vad som händer. Är detta också ett korrekt sätt att implementera useContext- och useReducer-hookar?
Svar #1
Ditt problem är att din kontext förväntas ha samma typ som initialState, men istället sätter du standardvärdet för din kontext till { state, dispatch }.
Det här är fel.
För att lösa det, ange antingen ditt contexts standardtyp till { state, dispatch } (jag tror att detta är vad du vill, eller ange contextens standardtyp till typeof initialState.
Här är lösningen.
Svar #2
Jag försöker skicka kontextvärdet av state och dispatch till barnkomponenten.
Typen MenuContext bestäms av initialState - de stämmer inte överens i ditt fall. Du kan deklarera en egen typ för kontextvärde:
type StoreApi = {
state: typeof initialState
dispatch: React.Dispatch<Action>
}
Definiera sedan MenuContext så här:
// undefined is just the default in case, you don't have a provider defined
export const MenuContext = React.createContext<StoreApi | undefined>(undefined)
// or if you want it more like your first example (I would prefer first variant)
export const MenuContext = React.createContext<StoreApi | typeof initialState>(initialState)
Är detta också ett korrekt sätt att implementera useContext- och useReducer-hooks?
Ovan är ett vanligt mönster för att initiera ett React-context. Ytterligare utvidgningspunkter:
1.) För närvarande renderar alla dina context-konsumenter om varje renderingsfas, eftersom value är en ny objektreferens varje gång. Om du har låg prestanda (ingen för tidig optimering), memoize value:
const value = React.useMemo(() => ({ state, dispatch }), [state])
2.) Skapa en anpassad useMenu-hook, som returnerar state, dispatch och andra användbara API-metoder.
function useMenu() {
const context = React.useContext(MenuContext);
if (context === undefined) throw new Error(`No provider for MenuContext given`);
const { state, dispatch } = context;
const myApiMethod = () => dispatch({ type: "SET_ACTIVE", label: "foo" })
return { ...context, myApiMethod };
}
const Client = () => {
const { state, dispatch, myApiMethod } = useMenu()
// ...
}
På detta sätt är MenuContext kapslat, det finns inget behov av att använda React.useContext(MenuContext) och klienten får ett skräddarsytt API. Ytterligare läsning: Hur man använder React Context effektivt och dess relaterade artiklar.
