Así que tengo un problema muy extraño con React Context + Typescript.
En el ejemplo anterior, puede ver que lo que estoy tratando de hacer realmente funciona. Esencialmente, estoy administrando el estado con el nuevo método useContext, y funciona perfectamente.
Sin embargo, cuando trato de hacer esto en mi caja, parece que no puede encontrar los valores de estado que se pasan a través de useReducer.
export function AdminStoreProvider(props: any) { const [state, dispatch] = useReducer(reducer, initialState); // state.isAuth is avail here // state.user is avail here const value = { state, dispatch }; // value.state.isAuth is avail here return ( /* value does not contain state once applied to the value prop */ <AdminStore.Provider value={value}>{props.children} </AdminStore.Provider> ); }Mensaje de error:
Type '{ state: { isAuth: boolean; user: string; }; dispatch: Dispatch<Actions>; }' is missing the following properties from type 'IState': isAuth, userTenga en cuenta que el código que estoy usando es exactamente lo que estoy usando en mi caja, incluso descargué el código de sandbox e intenté ejecutarlo, y no funciona.
estoy usando VSCode 1.31
Logré deducir que si cambio la forma en que creo mi contexto a partir de:
export const AdminStore = React.createContext(initialState);para
export const AdminStore = React.createContext(null);La propiedad de valor ya no arroja ese error.
Sin embargo, ahora useContext devuelve un error: el estado no existe en nulo. Y lo mismo si configuro defaultState para contexto en {}.
y por supuesto si yo
React.createContext();Luego, TS grita que no se proporciona ningún valor predeterminado.
En sandbox, las 3 versiones de creación del objeto de contexto funcionan bien.
Gracias de antemano por cualquier consejo.
Parece que se espera que el valor defaultValue para React.createContext sea del tipo:
interface IContextProps { state: IState; dispatch: ({type}:{type:string}) => void; } Una vez que se crea el objeto de Context para este tipo, por ejemplo, así:
export const AdminStore = React.createContext({} as IContextProps);El componente Provider React ya no debería quejarse del error.
Aquí está la lista de cambios:
admin-store.tsx
import React, { useReducer } from "react"; import { initialState, IState, reducer } from "./reducer"; interface IContextProps { state: IState; dispatch: ({type}:{type:string}) => void; } export const AdminStore = React.createContext({} as IContextProps); export function AdminStoreProvider(props: any) { const [state, dispatch] = useReducer(reducer, initialState); const value = { state, dispatch }; return ( <AdminStore.Provider value={value}>{props.children}</AdminStore.Provider> ); }Me divertí mucho con esto, así que pensé en compartir lo que se me ocurrió.
Los SidebarProps representan el estado del contexto. Todo lo demás, además de las acciones reductoras, se puede usar esencialmente como está.
Aquí hay un buen artículo que explica exactamente la misma solución (no en TypeScript): Mezcla de ganchos y Api de contexto
import React, { createContext, Dispatch, Reducer, useContext, useReducer } from 'react'; interface Actions { type: string; value: any; } interface SidebarProps { show: boolean; content: JSX.Element | null; } interface SidebarProviderProps { reducer: Reducer<SidebarProps, Actions>; initState: SidebarProps; } interface InitContextProps { state: SidebarProps; dispatch: Dispatch<Actions>; } export const SidebarContext = createContext({} as InitContextProps); export const SidebarProvider: React.FC<SidebarProviderProps> = ({ reducer, initState, children }) => { const [state, dispatch] = useReducer(reducer, initState); const value = { state, dispatch }; return ( <SidebarContext.Provider value={value}> {children} </SidebarContext.Provider> ); }; export const useSidebar = () => useContext(SidebarContext); const SidebarController: React.FC = ({ children }) => { const initState: SidebarProps = { show: false, content: null }; const reducer: Reducer<SidebarProps, Actions> = (state, action) => { switch (action.type) { case 'setShow': return { ...state, show: action.value }; case 'setContent': return { ...state, content: action.value }; default: return state; } }; return ( <SidebarProvider reducer={reducer} initState={initState}> {children} </SidebarProvider> ); }; export default SidebarController;