install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/state-context-pattern" ~/.claude/skills/intense-visions-harness-engineering-state-context-pattern-1e3e05 && rm -rf "$T"
manifest:
agents/skills/claude-code/state-context-pattern/SKILL.mdsource content
React Context Pattern
Manage shared state with React Context and useReducer for prop-drilling avoidance and scoped state
When to Use
- Sharing state across a component subtree without passing props through intermediate components
- Theme, locale, auth, or feature flag state that many components read but rarely changes
- Scoped state that should reset when a part of the tree unmounts
- Small applications where adding Zustand/Jotai/Redux is unnecessary overhead
Instructions
- Create a context with
. Provide a meaningful default orcreateContext
with a type assertion.null - Create a Provider component that encapsulates state logic (useState or useReducer) and passes values via
.value - Create a custom hook (
,useAuth
) that callsuseTheme
and throws if used outside the Provider.useContext - Split context into two: one for state, one for dispatch/actions. This prevents components that only dispatch from re-rendering on state changes.
- Memoize the context value with
to prevent unnecessary re-renders of consumers.useMemo - Keep context values small and stable — large objects that change frequently cause all consumers to re-render.
// 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; }
Details
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:
- Context re-renders ALL consumers when the value changes — no selector mechanism
- Context is scoped to a subtree — external stores are global
- Context requires no extra dependencies — it is built into React
- Context is best for low-frequency updates (auth, theme). For high-frequency updates (forms, animations), use Zustand or Jotai
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:
- Putting too much in context (entire app state) — this defeats React's component-level rendering model
- Creating context with an empty object default (
) — hides bugs where the provider is missingcreateContext({}) - Not memoizing the context value — a new object on every render causes all consumers to re-render
Source
https://react.dev/learn/passing-data-deeply-with-context
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.