Skip to main content

Context API — Shared State

Level: Beginner

ℹ️ What You'll Learn
  • Creating context with createContext
  • Provider component for sharing state
  • useContext hook to consume context
  • Avoiding prop drilling (passing through many levels)
  • Authentication context pattern
  • Theme/settings context for SMS
  • Context best practices and gotchas
  • When to use Context vs other state management

Why This Matters

Context API — Shared State is part of building maintainable React applications. You will use it when creating student dashboards, forms, tables, API-connected screens, routing flows, and reusable UI components.

Instead of passing props through many levels, use Context to share state globally.

The Problem

Beginners often write React code that works for a small demo but becomes difficult when data, forms, API calls, and reusable components grow. This lesson explains Context API — Shared State in a way that helps you build predictable UI for real .NET Web API projects.

Problem: Prop Drilling

function App() {
const [user, setUser] = useState({ name: 'Admin', role: 'Principal' });
return <Level1 user={user} />;
}

function Level1({ user }) {
return <Level2 user={user} />;
}

function Level2({ user }) {
return <Level3 user={user} />;
}

function Level3({ user }) {
return <Level4 user={user} />;
}

function Level4({ user }) {
return <p>User: {user.name}</p>;
}

Passing user through 4 levels is tedious. Context solves this.

Context Solution

import { createContext, useContext, useState } from 'react';

// 1. Create context
const UserContext = createContext();

// 2. Create provider component
function UserProvider({ children }) {
const [user, setUser] = useState({ name: 'Admin', role: 'Principal' });

return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}

// 3. Create custom hook to use context
function useUser() {
return useContext(UserContext);
}

// 4. Wrap app with provider
function App() {
return (
<UserProvider>
<Level1 />
</UserProvider>
);
}

// 5. Use context anywhere
function Level4() {
const { user } = useUser();
return <p>User: {user.name}</p>;
}

No prop drilling!

Real Example: Authentication Context

const AuthContext = createContext();

function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
// Check if user is logged in
fetch('/api/me')
.then(r => r.json())
.then(data => setUser(data))
.catch(() => setUser(null))
.finally(() => setLoading(false));
}, []);

const login = async (email, password) => {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
const data = await response.json();
setUser(data);
return data;
};

const logout = () => {
setUser(null);
};

return (
<AuthContext.Provider value={{ user, loading, login, logout }}>
{children}
</AuthContext.Provider>
);
}

function useAuth() {
return useContext(AuthContext);
}

// Usage
function Navbar() {
const { user, logout } = useAuth();

if (!user) return <p>Not logged in</p>;

return (
<nav>
<p>Welcome, {user.name}</p>
<button onClick={logout}>Logout</button>
</nav>
);
}

Theme Context

const ThemeContext = createContext();

function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');

const toggleTheme = () => {
setTheme(t => t === 'light' ? 'dark' : 'light');
};

return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}

function useTheme() {
return useContext(ThemeContext);
}

// Usage
function App() {
const { theme } = useTheme();

return (
<div style={{ background: theme === 'light' ? 'white' : 'black' }}>
<Navbar />
<Content />
</div>
);
}

function Navbar() {
const { toggleTheme } = useTheme();
return <button onClick={toggleTheme}>Toggle Theme</button>;
}

School Context (Multi-context Example)

// User context
const UserContext = createContext();

function UserProvider({ children }) {
const [currentUser, setCurrentUser] = useState(null);

return (
<UserContext.Provider value={{ currentUser, setCurrentUser }}>
{children}
</UserContext.Provider>
);
}

// School context
const SchoolContext = createContext();

function SchoolProvider({ children }) {
const [currentSchool, setCurrentSchool] = useState(null);

return (
<SchoolContext.Provider value={{ currentSchool, setCurrentSchool }}>
{children}
</SchoolContext.Provider>
);
}

// Combined provider
function AppProviders({ children }) {
return (
<UserProvider>
<SchoolProvider>
{children}
</SchoolProvider>
</UserProvider>
);
}

// Use in app
function App() {
return (
<AppProviders>
<Navbar />
<Routes>{/* routes */}</Routes>
</AppProviders>
);
}

function StudentDetail() {
const { currentUser } = useContext(UserContext);
const { currentSchool } = useContext(SchoolContext);

return (
<div>
<p>School: {currentSchool?.name}</p>
<p>User: {currentUser?.name}</p>
</div>
);
}

Best Practices

1. Create Custom Hook

Always wrap useContext in a custom hook:

// ❌ Bad
function MyComponent() {
const value = useContext(UserContext);
}

// ✓ Good
function useUser() {
return useContext(UserContext);
}

function MyComponent() {
const { user } = useUser();
}

2. Separate Provider Files

// auth/AuthContext.js
export const AuthContext = createContext();

export function AuthProvider({ children }) {
// ...
}

export function useAuth() {
return useContext(AuthContext);
}

// Then import
import { useAuth } from './auth/AuthContext';

3. Combine Multiple Contexts

// Don't nest too deep
<AuthProvider>
<ThemeProvider>
<SchoolProvider>
<App />
</SchoolProvider>
</ThemeProvider>
</AuthProvider>

// Better: combine
<CombinedProviders>
<App />
</CombinedProviders>

When to Use Context

Use Context for:

  • Authentication/User
  • Current theme
  • Language/localization
  • Current school/organization
  • Global notifications

Don't use Context for:

  • Form state (local state is fine)
  • List data (changes frequently)
  • Complex state (use Redux/Zustand)

Key Takeaways

  • Context solves prop drilling
  • Create custom hooks for contexts
  • Provider wraps components needing access
  • useContext to access values
  • Next: Custom Hooks for reusable logic
⚠️ Context Mistakes
  1. Not memoizing context value — Causes unnecessary re-renders
  2. Context for everything — Use local state when appropriate
  3. Too many nested providers — Hard to read
  4. Changing context frequently — All consumers re-render
💡 Memoize Context Value
function UserProvider({ children }) {
const [user, setUser] = useState(null);

const value = useMemo(() => ({ user, setUser }), [user]);

return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
);
}
🤖Use AI to Learn Faster

Use ChatGPT, Claude, or Copilot to go deeper on Context API. Try these prompts:

  • "What problem does Context solve?"
  • "How do you create a custom hook for Context?"
  • "When should you use Context vs. local state?"
  • "Quiz me on Context API"

💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.

Quick Definitions

  • Context API — Shared State - The main React concept explained in this lesson.
  • Component - A reusable function that returns UI.
  • Props/state/effects - Core React ideas used to pass data, remember data, and run side effects.
  • Real project usage - How this appears in forms, dashboards, routes, and API-connected pages.

Common Mistakes

  • Copying React code without understanding data flow
  • Mutating arrays or objects directly instead of creating new values
  • Forgetting keys, dependencies, loading states, or error states where needed
  • Putting too much logic in one component
  • Not testing the screen with realistic School Management System data

Practice Task

Create a small React example using Context API — Shared State. Keep it focused on one School Management System screen.

Suggested practice:

  1. Build a small component or page for students, attendance, marks, or fees.
  2. Pass realistic data into the component.
  3. Add one success state and one empty/error state where relevant.
  4. Explain the data flow in your own words.
  5. Rebuild the same example once without looking at the article.

Quick Revision

QuestionAnswer
What is the main idea?Understand and apply Context API — Shared State in React.
Where is it used?Student dashboards, forms, tables, routes, and API-connected screens.
What should beginners focus on?Clear components, predictable data flow, and small examples.
What is the best debugging habit?Check props, state, render output, and browser console step by step.
🎯 How would you explain Context API — Shared State in an interview?

Context API — Shared State is a React concept used to build clear, reusable, and predictable user interfaces. I would explain the problem it solves, show a small component example, and mention a common mistake beginners should avoid.

🎯 Where is this used in a real React project?

It is used in screens like student lists, admission forms, attendance dashboards, marks reports, routing pages, and API-connected admin panels.

Next Article

Custom Hooks — Reusable Logic ->

nexcoding.in