Level 9: Ultimate + DevTools
Heads Up
This code is really high-level and reaching the point where you'd just use Zustand or some other state management library. You would probably not ever need to do this yourself and even if you could, you'd still want to use Zustand just because it is open source, has a team supporting it, and most importantly, not just you!
Consider yourself warned.
Build the Ultimate Context with Logger
import {
createContext,
useContext,
useReducer,
ReactNode,
Dispatch,
} from 'react';
type DevtoolsHook<State> = (state: State) => void;
export function createUltimateContext<State, Actions = undefined>(options: {
displayName: string;
reducer?: React.Reducer<State, any>;
initialState?: State;
devtools?: DevtoolsHook<State>; // optional custom log function
enableLogger?: boolean; // simple console.log fallback
}) {
const StateContext = createContext<State | undefined>(undefined);
const ActionsContext = createContext<any>(undefined); // Actions or Dispatch
StateContext.displayName = `${options.displayName}State`;
ActionsContext.displayName = `${options.displayName}Actions`;
const useState = () => {
const context = useContext(StateContext);
if (!context) {
throw new Error(`[${options.displayName}] Missing State Provider`);
}
return context;
};
const useActions = (): Actions extends undefined ? Dispatch<any> : Actions => {
const context = useContext(ActionsContext);
if (!context) {
throw new Error(`[${options.displayName}] Missing Actions Provider`);
}
return context;
};
const Provider = (props: any) => {
if (options.reducer) {
const [state, dispatch] = useReducer(options.reducer, options.initialState!);
if (options.devtools) {
options.devtools(state);
} else if (options.enableLogger) {
console.log(`[${options.displayName}]`, state);
}
return (
<StateContext.Provider value={state}>
<ActionsContext.Provider value={dispatch}>
{props.children}
</ActionsContext.Provider>
</StateContext.Provider>
);
}
// Manual state + actions pattern
const { state, actions, children } = props;
if (options.devtools) {
options.devtools(state);
} else if (options.enableLogger) {
console.log(`[${options.displayName}]`, state);
}
return (
<StateContext.Provider value={state}>
<ActionsContext.Provider value={actions}>
{children}
</ActionsContext.Provider>
</StateContext.Provider>
);
};
return {
Provider,
useState,
useActions,
};
}
Quick console.log()
Support (no setup)
const {
Provider: CounterProvider,
useState: useCounterState,
useActions: useCounterDispatch,
} = createUltimateContext({
displayName: 'Counter',
reducer: (state, action) => {
switch (action.type) {
case 'inc':
return { count: state.count + 1 };
case 'reset':
return { count: 0 };
default:
return state;
}
},
initialState: { count: 0 },
enableLogger: true, // ✅ logs on every state update
});
Custom Logging
const {
Provider: AuthProvider,
useState: useAuthState,
useActions: useAuthActions,
} = createUltimateContext({
displayName: 'Auth',
initialState: { user: null },
devtools: (state) => {
console.log('%c[Auth Devtools]', 'color: green', state);
},
});
Example Implementation
Create your Context
// CounterContext.tsx
import { createUltimateContext } from './createUltimateContext';
type CounterState = {
count: number;
};
type CounterAction =
| { type: 'inc' }
| { type: 'dec' }
| { type: 'reset' };
const counterReducer = (state: CounterState, action: CounterAction): CounterState => {
switch (action.type) {
case 'inc':
return { count: state.count + 1 };
case 'dec':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
return state;
}
};
const {
Provider: CounterProvider,
useState: useCounterState,
useActions: useCounterDispatch,
} = createUltimateContext<CounterState>({
displayName: 'Counter',
reducer: counterReducer,
initialState: { count: 0 },
enableLogger: true, // ✅ Console logs on each state update
});
export { CounterProvider, useCounterState, useCounterDispatch };
Wrap Target Component in Provider
import { CounterProvider } from './CounterContext';
import { Counter } from './Counter';
export function ParentComponent() {
return (
<CounterProvider>
<Counter />
</CounterProvider>
);
}
Use it in a component
// Counter.tsx
import { useCounterState, useCounterDispatch } from './CounterContext';
export function Counter() {
const { count } = useCounterState();
const dispatch = useCounterDispatch();
return (
<div>
<h2>Count: {count}</h2>
<button onClick={() => dispatch({ type: 'inc' })}>+</button>
<button onClick={() => dispatch({ type: 'dec' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}