Created
December 30, 2020 13:27
-
-
Save lopezjurip/72a3ac2cd565fc47cb086e70c01ab520 to your computer and use it in GitHub Desktop.
React useReducer useContext with Typescript types
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | |
); | |
} |
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
Thanks, it is helpful !