Ojasa Mirai

Ojasa Mirai

ReactJS

Loading...

Learning Level

🟢 Beginner🔵 Advanced
🌍 Context API Basics📦 Creating Context🔌 Context.Provider🔍 useContext Hook🎯 Passing Data with Context🔄 Context with useState⚡ Context Performance🏗️ Context Best Practices
Reactjs/Context/Usecontext Hook

🔍 Advanced useContext Hook — Building Custom Context Consumption Patterns

Advanced Context consumption involves understanding context from multiple angles: custom hooks for ergonomic access, subscriber patterns for reactive updates, and error boundaries for robust error handling.


🎯 Custom Hooks Over Direct useContext

Custom hooks provide abstraction over raw `useContext()` calls. They enable validation, selective memoization, and graceful error handling:

import { useContext, useCallback, useMemo } from "react";

interface AppContextType {
  user: { id: string; name: string } | null;
  setUser: (user: AppContextType["user"]) => void;
  preferences: { theme: string; language: string };
  updatePreferences: (prefs: Partial<AppContextType["preferences"]>) => void;
}

const AppContext = createContext<AppContextType | undefined>(undefined);

// ❌ Direct useContext requires null checks everywhere
function BadComponent() {
  const context = useContext(AppContext);
  if (!context) throw new Error("Not in provider");
  const { user } = context;
  return <div>{user?.name}</div>;
}

// ✅ Custom hook encapsulates logic
function useApp() {
  const context = useContext(AppContext);
  if (!context) {
    throw new Error("useApp must be used within AppProvider");
  }
  return context;
}

function GoodComponent() {
  const { user } = useApp();
  return <div>{user?.name}</div>;
}

// Advanced: Selector hook for specific values
function useAppValue<T>(selector: (app: AppContextType) => T): T {
  const app = useApp();
  return useMemo(() => selector(app), [app, selector]);
}

// Components only re-render when selected value changes
function UserName() {
  const name = useAppValue((app) => app.user?.name);
  return <h1>{name}</h1>;
}

function UserEmail() {
  const email = useAppValue((app) => app.user?.email);
  return <p>{email}</p>;
}

// Changing theme doesn't re-render UserName or UserEmail
// because they only selected the user's name/email

💡 Advanced Custom Hook Patterns

Building custom hooks that handle multiple concerns simultaneously:

// 1. Hook with loading and error states
function useAppAsync<T,>(
  fn: () => Promise<T>,
  dependencies: any[]
): { data: T | null; loading: boolean; error: Error | null } {
  const { fetchData } = useApp();
  const [state, setState] = useState<{
    data: T | null;
    loading: boolean;
    error: Error | null;
  }>({ data: null, loading: false, error: null });

  useEffect(() => {
    setState({ data: null, loading: true, error: null });
    fn()
      .then((data) => setState({ data, loading: false, error: null }))
      .catch((error) => setState({ data: null, loading: false, error }));
  }, dependencies);

  return state;
}

// 2. Hook for dependent state
function useAppUserData() {
  const { user } = useApp();
  const { data: userData } = useAppAsync(
    () => (user ? fetch(`/api/users/${user.id}`).then((r) => r.json()) : Promise.resolve(null)),
    [user?.id]
  );

  return userData;
}

// 3. Hook combining multiple context values
function useAppState() {
  const app = useApp();

  return useMemo(() => ({
    isAuthenticated: !!app.user,
    user: app.user,
    theme: app.preferences.theme,
    setTheme: (theme: string) =>
      app.updatePreferences({ theme }),
  }), [app]);
}

// 4. Hook with caching
const useAppCache = (() => {
  const cache = new Map();

  return function <T,>(key: string, fn: () => T): T {
    if (!cache.has(key)) {
      cache.set(key, fn());
    }
    return cache.get(key);
  };
})();

<details>

<summary>📚 More Examples</summary>

// Example 1: Hook with lifecycle management
function useAppListener(
  callback: (app: AppContextType) => void,
  dependencies: any[] = []
) {
  const app = useApp();

  useEffect(() => {
    callback(app);
  }, [app, ...dependencies]);
}

// Example 2: Hook for mutations
function useAppMutations() {
  const app = useApp();

  return useMemo(() => ({
    updateUser: (user: Partial<AppContextType["user"]>) => {
      app.setUser({ ...app.user, ...user });
    },
    updateTheme: (theme: string) => {
      app.updatePreferences({ theme });
    },
  }), [app]);
}

// Example 3: Hook with derived state
function useAppDerivedState() {
  const { user, preferences } = useApp();

  return useMemo(() => ({
    isDarkMode: preferences.theme === "dark",
    isAdmin: user?.role === "admin",
    displayName: user?.name.toUpperCase(),
  }), [user, preferences]);
}

// Example 4: Hook with batch updates
function useAppBatchUpdate() {
  const app = useApp();

  return useCallback((updates: Partial<AppContextType>) => {
    Object.entries(updates).forEach(([key, value]) => {
      if (key === "user") app.setUser(value as any);
      if (key === "preferences") app.updatePreferences(value as any);
    });
  }, [app]);
}

</details>

🎨 Real-World: Error Boundaries with Context

Context works best when paired with error boundaries for resilient applications:

interface ErrorBoundaryProps {
  children: ReactNode;
  fallback?: (error: Error, reset: () => void) => ReactNode;
}

interface ErrorBoundaryState {
  hasError: boolean;
  error: Error | null;
}

class ContextErrorBoundary extends React.Component<
  ErrorBoundaryProps,
  ErrorBoundaryState
> {
  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    return { hasError: true, error };
  }

  render() {
    if (this.state.hasError && this.state.error) {
      return (
        this.props.fallback?.(
          this.state.error,
          () => this.setState({ hasError: false, error: null })
        ) || (
          <div>
            <h1>Something went wrong</h1>
            <p>{this.state.error.message}</p>
          </div>
        )
      );
    }

    return this.props.children;
  }
}

// Usage with context
function App() {
  return (
    <ContextErrorBoundary
      fallback={(error, reset) => (
        <div>
          <h2>Context Error</h2>
          <p>{error.message}</p>
          <button onClick={reset}>Try again</button>
        </div>
      )}
    >
      <AppProvider>
        <MainApp />
      </AppProvider>
    </ContextErrorBoundary>
  );
}

// Advanced: Context-aware error boundary
function useErrorHandler() {
  const { reportError } = useApp();

  return useCallback((error: Error) => {
    reportError(error);
    throw error;
  }, []);
}

function SafeComponent() {
  const handleError = useErrorHandler();

  return (
    <ContextErrorBoundary
      fallback={(error) => <div>Error: {error.message}</div>}
    >
      <RiskyComponent onError={handleError} />
    </ContextErrorBoundary>
  );
}

📊 Consumer Pattern Comparison

PatternUse CaseBenefit
Direct useContextSimple accessMinimal boilerplate
Custom hookValidationClear errors
Selector hookPerformancePrevents re-renders
Error boundarySafetyGraceful failures
Listener hookReactionsSide effects

🔑 Key Takeaways

  • ✅ Wrap `useContext()` in custom hooks for validation and clarity
  • ✅ Create selector hooks to optimize re-renders
  • ✅ Build hook composition for complex state logic
  • ✅ Use error boundaries to catch Context errors
  • ✅ Implement listener patterns for reactive updates
  • ✅ Cache expensive context computations
  • ✅ Derive state from context for clean interfaces
  • ✅ Handle async operations gracefully with custom hooks

Ready to practice? Challenges | Next: Complex Context Data


Resources

Python Docs

Ojasa Mirai

Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.

Learn Deep • Build Real • Verify Skills • Launch Forward

Courses

PythonFastapiReactJSCloud

© 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn