← Blog
React Reducer Patterns
December 28, 2022 • 2 min read
The reducer pattern is one of the commonly used patterns in React. Here are some patterns that can be useful for you when using it.
Toogle Boolean
The hooks is usefull for controlling toggle or other elements with a boolean state.
interface ToggleProps { initialState?: boolean; } function useToggle({ initialState = false }: ToogleProps) { const [enabled, setEnabled] = React.useReducer((previous) => !previous, initialState) return [enabled, setEnabled] }
Select or deselect an item
interface SelectionProps<T> { isEqual?: (prev: T | undefined, next: T) => boolean; initialState?: T | undefined; } function useSelection<T>({ isEqual = (prev, next) => prev === next, initialState = undefined }: SelectionProps = {}) { const [selection, setSelection] = React.useReducer( (prev: T | undefined, next: T) => (isEqual(prev, next) ? undefined : next), initialState, ) return [select, setSelection] }
Selecting multiple items
interface MultiSelectionProps<T> { isEqual?: (prev: T, next: T) => boolean; initialState?: T[]; } function useMultipleSelection<T>({ isEqual = (prev, next) => prev === next, initialState = [], }: MultipleSelectionProps) { const [selection, setSelection] = React.useReducer( (prev: T[], next: T[]) => { const index = prev.findIndex((x) => isEqual(x, next)) return index === -1 ? [...prev, next] : prev.filter((_, i) => i !== index) }, initialState) return [selection, setSelection] }
Menu or exclusive Value
type Menu = undefined | 'file' | 'edit' | 'view' // undefined mean closed const [openMenu, tap] = React.useReducer( (current: Menu, action: Menu) => { return action === current ? null : action }, undefined)
Jitter Generator
Inspired by AWS: Exponential Backoff And Jitter
const baseMs = 5 const capMs = 2000 const [{ attempt, delayMs }, dispatch] = React.useReducer( (state, random) => { return { attempt: state.attempt + 1, delayMs: random * Math.min(capMs, baseMs * Math.pow(2, attempt)), } }, { attempt: 0, delay: baseMs }, ) const nextAttempt = React.useCallback(() => dispatch(Math.random()), [dispatch])
Logical Clock or Force Update
const [t, tick] = React.useReducer((n) => n + 1, 0) function useForceUpdate() { const [ignored, forceUpdate] = React.useReducer((n) => n + 1, 0) return [ignored, forceUpdate] }