I am "translating" this react example into typescript: https://react.dev/learn/scaling-up-with-reducer-and-context#step-1-create-the-context
without success so far! The UserUpdateContext "value" is giving me this error in Visual Studio:
(Type 'ActionDispatch<[action: Action]>' is not assignable to type '[action: Action]'.ts(2322)
index.d.ts(536, 9): The expected type comes from property 'value' which is declared here on type 'IntrinsicAttributes & ProviderProps<[action: Action] | null>')
<UserUpdateContext.Provider value={dispatch}>
Also useUserDispatch() is returning an action rather than a function accepting an action as parameter.
I tried numerous variations but any typescript / react example is doing it a bit different and no one has used ActionDispatch or Dispatch types here.
I am stuck :)
import { createContext, useContext, useReducer, type ActionDispatch, type Dispatch, type ReactNode } from 'react';
import {type User} from "./user"
import { UserFactory } from './unit';
export type Action =
| { type: "created", value: User}
| { type: "changed" | "deleted", value: any }
const UserContext = createContext<User|null>( null);
// ActionDispatch<[action: Action]
// const UserUpdateContext = createContext<any>( {type: "created", value: UserFactory.createUser()});
const UserUpdateContext = createContext< [action: Action]|null>( null);
export function UserProvider({ children }:{children: ReactNode | ReactNode[]}) {
const [user, dispatch] = useReducer(
userReducer, // reducer
UserFactory.createUser() // initial state
);
return (
<UserContext value={user}>
<UserUpdateContext.Provider value={dispatch}>
{children}
</UserUpdateContext.Provider>
</UserContext>
)
}
export function useUser() {
return useContext(UserContext);
}
export function useUserDispatch() {
return useContext(UserUpdateContext);
}
function userReducer(user:User, action:Action) {
switch (action.type) {
case "created": {
return action.value
}
// other stuff
case 'changed': {
}
}
}
useReducer is defined as:
export function useReducer<S, I, A>( reducer: (S, A) => S, initialArg: I, init?: I => S, ): [S, Dispatch<A>] { const dispatcher = resolveDispatcher(); return dispatcher.useReducer(reducer, initialArg, init); }
The return type of your useReducer(userReducer, UserFactory.createUser()) is an array of your User state type and your Action type wrapped in React's Dispatch, e.g.:
[User, Dispatch<Action>]
So these are the types you should provide to your context providers respectively, e.g.:
import {
createContext,
type Dispatch
} from "react";
type Action =
| { type: "created"; value: User }
| { type: "changed" | "deleted"; value: any };
const UserContext = createContext<User | null>(null);
const UserUpdateContext = createContext<Dispatch<Action> | null>(null);
Your UserProvider component might also be a bit malformed. If you are using React 19 rendering the context directly will render the Provider component, otherwise if you are using an older version it should use UserContext.Provider to provide the User value instead of just UserContext.
import {
PropsWithChildren,
useReducer,
} from "react";
function UserProvider({ children }: PropsWithChildren) {
const [user, dispatch] = useReducer(userReducer, UserFactory.createUser());
return (
<UserContext.Provider value={user}>
<UserUpdateContext.Provider value={dispatch}>
{children}
</UserUpdateContext.Provider>
</UserContext.Provider>
);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With