From harness-claude
Manages shared state across React component subtrees using Context and useReducer to avoid prop drilling. For auth, themes, locales, feature flags in small apps.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Manage shared state with React Context and useReducer for prop-drilling avoidance and scoped state
Implements React Context pattern to share infrequent state like themes, users, or flags across component trees without prop drilling. Includes typed providers, safe hooks, and performance tips.
Guides React Context patterns for state management to share state across component trees without prop drilling. Includes TypeScript examples for auth providers.
Implements React component patterns: compound components with context, render props, HOCs, custom hooks, provider with reducer, controlled/uncontrolled, prop getters, state reducer.
Share bugs, ideas, or general feedback.
Manage shared state with React Context and useReducer for prop-drilling avoidance and scoped state
createContext. Provide a meaningful default or null with a type assertion.value.useAuth, useTheme) that calls useContext and throws if used outside the Provider.useMemo to prevent unnecessary re-renders of consumers.// contexts/auth-context.tsx
import { createContext, useContext, useReducer, useMemo, ReactNode } from 'react';
interface User { id: string; name: string; }
interface AuthState {
user: User | null;
isAuthenticated: boolean;
}
type AuthAction =
| { type: 'LOGIN'; user: User }
| { type: 'LOGOUT' };
type AuthDispatch = (action: AuthAction) => void;
const AuthStateContext = createContext<AuthState | null>(null);
const AuthDispatchContext = createContext<AuthDispatch | null>(null);
function authReducer(state: AuthState, action: AuthAction): AuthState {
switch (action.type) {
case 'LOGIN':
return { user: action.user, isAuthenticated: true };
case 'LOGOUT':
return { user: null, isAuthenticated: false };
default:
return state;
}
}
export function AuthProvider({ children }: { children: ReactNode }) {
const [state, dispatch] = useReducer(authReducer, {
user: null,
isAuthenticated: false,
});
// Memoize to prevent re-renders when AuthProvider's parent re-renders
const stateValue = useMemo(() => state, [state]);
return (
<AuthStateContext.Provider value={stateValue}>
<AuthDispatchContext.Provider value={dispatch}>
{children}
</AuthDispatchContext.Provider>
</AuthStateContext.Provider>
);
}
export function useAuthState(): AuthState {
const context = useContext(AuthStateContext);
if (!context) throw new Error('useAuthState must be used within AuthProvider');
return context;
}
export function useAuthDispatch(): AuthDispatch {
const context = useContext(AuthDispatchContext);
if (!context) throw new Error('useAuthDispatch must be used within AuthProvider');
return context;
}
// Usage
function LoginButton() {
const dispatch = useAuthDispatch(); // Does NOT re-render when auth state changes
return <button onClick={() => dispatch({ type: 'LOGIN', user: { id: '1', name: 'Alice' } })}>Login</button>;
}
function UserBadge() {
const { user } = useAuthState(); // Re-renders when auth state changes
return user ? <span>{user.name}</span> : null;
}
Why split state and dispatch contexts: When the context value is { state, dispatch }, every consumer re-renders when state changes — even components that only call dispatch. Separate contexts solve this.
Context vs external state libraries:
useReducer vs useState: Use useReducer when state has multiple sub-values, when the next state depends on the previous state, or when you want to decouple state logic from the Provider component.
Performance optimization: For large subtrees, wrap children in React.memo or use the split-context pattern. For truly high-performance needs, switch to an external store.
Testing:
function renderWithAuth(ui: ReactNode, initialState?: Partial<AuthState>) {
return render(<AuthProvider>{ui}</AuthProvider>);
}
Common mistakes:
createContext({})) — hides bugs where the provider is missinghttps://react.dev/learn/passing-data-deeply-with-context