useReducerWithLocalStorage Hook
A custom React Hook combining useReducer with localStorage persistence (TypeScript support)
Code
import { useReducer, useEffect, Reducer } from 'react';
type ReducerAction<T> = {
type: string;
payload?: T;
};
function useReducerWithLocalStorage<S, A extends ReducerAction<unknown>>(
reducer: Reducer<S, A>,
initialState: S,
key: string
): [S, (action: A) => void] {
// Initialize state from localStorage or initialState
const getInitialState = (): S => {
__TOKEN_26__ (typeof window === 'undefined') return initialState;
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialState;
} __TOKEN_32__ (error) {
console.warn(`Error reading localStorage key "${key}":`, error);
return initialState;
}
};
const [state, dispatch] = useReducer(reducer, initialState, getInitialState);
// Persist state to localStorage on change
useEffect(() => {
__TOKEN_35__ (typeof window !== 'undefined') {
try {
window.localStorage.setItem(key, JSON.stringify(state));
} __TOKEN_38__ (error) {
console.warn(`Error setting localStorage key "${key}":`, error);
}
}
}, [state, key]);
return [state, dispatch];
}
// Usage example
// type CounterState = { count: number };
// type CounterAction = { type: 'INCREMENT' | 'DECREMENT' | 'RESET' };
// const counterReducer = (state: CounterState, action: CounterAction): CounterState => {
// switch (action.type) {
// case 'INCREMENT': return { count: state.count + 1 };
// case 'DECREMENT': return { count: state.count - 1 };
// case 'RESET': return { count: 0 };
// default: return state;
// }
// };
// const [state, dispatch] = useReducerWithLocalStorage(counterReducer, { count: 0 }, 'counter-state');