Skip to content

Instantly share code, notes, and snippets.

@lopezjurip
Created December 30, 2020 13:27
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lopezjurip/72a3ac2cd565fc47cb086e70c01ab520 to your computer and use it in GitHub Desktop.
Save lopezjurip/72a3ac2cd565fc47cb086e70c01ab520 to your computer and use it in GitHub Desktop.
React useReducer useContext with Typescript types
import React, { useReducer, useContext, Dispatch } from "react";
import "./styles.css";
// Local types, example: box, product, venue, etc
type MyItem = {
id: number;
name: string;
};
// Reducer types
type ActionMap<M extends { [index: string]: any }> = {
[Key in keyof M]: M[Key] extends undefined
? { type: Key }
: { type: Key; payload: M[Key] };
};
export enum Action {
AddItem = "ADD_ITEM"
}
type ActionPayloads = {
[Action.AddItem]: { name: string };
};
export type StoreActions = ActionMap<ActionPayloads>[keyof ActionMap<
ActionPayloads
>];
type StoreState = {
items: MyItem[];
};
// App global state AKA "store"
const initialState: StoreState = { items: [{ id: 0, name: "Initial" }] };
const WidgetContext = React.createContext<[StoreState, Dispatch<StoreActions>]>(
[initialState, () => null]
);
function reducer(state: StoreState, action: StoreActions): StoreState {
switch (action.type) {
case Action.AddItem: {
const last = state.items[state.items.length - 1];
const newItem = { id: last.id + 1, name: action.payload.name };
return {
items: [...state.items, newItem]
};
}
default: {
throw new Error("Action not recognized");
}
}
}
export default function App() {
// Create global state
const [store, dispatch] = useReducer(reducer, initialState);
return (
<WidgetContext.Provider value={[store, dispatch]}>
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Component1 />
<Component2 />
</div>
</WidgetContext.Provider>
);
}
function Component1() {
// Subscribe to store
const [store, dispatch] = useContext(WidgetContext);
function handleClick() {
const name = String(new Date().getTime());
dispatch({ type: Action.AddItem, payload: { name } });
}
return <button onClick={handleClick}>Add item</button>;
}
function Component2() {
// Subscribe to store
const [store, dispatch] = useContext(WidgetContext);
return (
<ul>
{store.items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
@melon-husk
Copy link

Thanks, it is helpful !

@lopezjurip
Copy link
Author

Made this new version which is easier to understand: https://gist.github.com/lopezjurip/510ed0116935417d17c05e3e7392a23c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment