React Context API Basics

Introduction to React Context API

In React, managing state and passing data from a parent component to deeply nested child components can sometimes become cumbersome. This problem, commonly known as prop drilling, occurs when data must be passed through multiple layers of components that do not necessarily need it.

The Context API, introduced in React 16.3, provides a built-in solution for global state management. It allows developers to share data across multiple components without passing props manually at every level. Context is particularly useful for theme management, user authentication, language settings, and other global application data.

In this post, we will cover:

  • How to create a context
  • Providing context values
  • Consuming context in child components
  • A practical example: building a theme switcher (dark/light mode)

Creating a Context

The first step in using the Context API is creating a context object using React.createContext(). This object serves as a central store for the data you want to share globally.

Basic Syntax

import React from 'react';

// Create a context with an optional default value
const MyContext = React.createContext(defaultValue);

export default MyContext;
  • defaultValue is optional and used when a component consumes the context without a provider.
  • The MyContext object includes two main components:
    1. Provider: Wraps components and provides context values.
    2. Consumer: Accesses context values inside child components.

Example: Creating a User Context

import React from 'react';

// Creating a context to store user data
const UserContext = React.createContext({
  name: "Guest",
  role: "visitor"
});

export default UserContext;

Providing Context Values

Once the context is created, we use the Provider component to supply values to all child components. The Provider wraps the component tree and makes the context available to all nested components.

Syntax

<Context.Provider value={/* any value */}>
  {/* child components */}
</Context.Provider>
  • The value prop can contain any data type: object, array, number, function, etc.
  • All children wrapped inside the provider can access the context value.

Example: Providing User Data

import React, { useState } from 'react';
import UserContext from './UserContext';
import UserProfile from './UserProfile';

function App() {
  const [user, setUser] = useState({ name: "Alice", role: "admin" });

  return (
&lt;UserContext.Provider value={user}&gt;
  &lt;UserProfile /&gt;
&lt;/UserContext.Provider&gt;
); } export default App;

Explanation:

  • UserContext.Provider wraps UserProfile.
  • Any component inside the provider can now consume the user object.

Consuming Context in Child Components

There are several ways to access context values in child components:

1. Using useContext Hook (Recommended for Functional Components)

import React, { useContext } from 'react';
import UserContext from './UserContext';

function UserProfile() {
  const user = useContext(UserContext);

  return (
&lt;div&gt;
  &lt;h2&gt;User Profile&lt;/h2&gt;
  &lt;p&gt;Name: {user.name}&lt;/p&gt;
  &lt;p&gt;Role: {user.role}&lt;/p&gt;
&lt;/div&gt;
); } export default UserProfile;

Explanation:

  • useContext(UserContext) retrieves the current context value.
  • No need to pass props through intermediate components.
  • Automatically subscribes to context changes, re-rendering the component when the value updates.

2. Using Context Consumer Component

For class components or alternative usage:

import React from 'react';
import UserContext from './UserContext';

function UserProfile() {
  return (
&lt;UserContext.Consumer&gt;
  {user =&gt; (
    &lt;div&gt;
      &lt;h2&gt;User Profile&lt;/h2&gt;
      &lt;p&gt;Name: {user.name}&lt;/p&gt;
      &lt;p&gt;Role: {user.role}&lt;/p&gt;
    &lt;/div&gt;
  )}
&lt;/UserContext.Consumer&gt;
); } export default UserProfile;

Explanation:

  • UserContext.Consumer uses a render prop to access context values.
  • Less common in modern React since useContext is more concise.

Example: Theme Switcher (Dark/Light Mode)

Let’s build a practical example using the Context API: a theme switcher. The theme context will allow any component to access and toggle between dark and light themes.

Step 1: Create Theme Context

import React from 'react';

const ThemeContext = React.createContext({
  theme: "light",
  toggleTheme: () => {}
});

export default ThemeContext;

Step 2: Provide Theme Context in App Component

import React, { useState } from 'react';
import ThemeContext from './ThemeContext';
import ThemedButton from './ThemedButton';

function App() {
  const [theme, setTheme] = useState("light");

  const toggleTheme = () => {
setTheme(prevTheme =&gt; (prevTheme === "light" ? "dark" : "light"));
}; return (
&lt;ThemeContext.Provider value={{ theme, toggleTheme }}&gt;
  &lt;div style={{
    backgroundColor: theme === "light" ? "#fff" : "#333",
    color: theme === "light" ? "#000" : "#fff",
    padding: "20px"
  }}&gt;
    &lt;h1&gt;React Theme Switcher&lt;/h1&gt;
    &lt;ThemedButton /&gt;
  &lt;/div&gt;
&lt;/ThemeContext.Provider&gt;
); } export default App;

Explanation:

  • theme state stores the current theme.
  • toggleTheme function toggles between light and dark themes.
  • ThemeContext.Provider passes both theme and toggleTheme to child components.

Step 3: Consume Theme in Child Component

import React, { useContext } from 'react';
import ThemeContext from './ThemeContext';

function ThemedButton() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
&lt;button
  onClick={toggleTheme}
  style={{
    padding: "10px 20px",
    backgroundColor: theme === "light" ? "#000" : "#fff",
    color: theme === "light" ? "#fff" : "#000",
    border: "none",
    cursor: "pointer"
  }}
&gt;
  Toggle Theme
&lt;/button&gt;
); } export default ThemedButton;

Explanation:

  • useContext(ThemeContext) provides access to the current theme and toggle function.
  • The button style changes dynamically based on the theme.
  • Clicking the button toggles the theme for all components consuming the context.

Benefits of Using Context API

  1. Avoids Prop Drilling
    • Components can access context directly without passing props through multiple layers.
  2. Centralized State Management
    • Ideal for managing global data like themes, user authentication, or language settings.
  3. Improves Maintainability
    • Makes it easy to modify and extend global state without changing intermediate components.
  4. Seamless Integration with Hooks
    • Works well with useState, useReducer, and custom hooks.

Best Practices for Context API

  • Use Context for Global Data Only: Don’t use it for every small state; keep it for shared or persistent data.
  • Separate Contexts for Different Data: Avoid combining unrelated data in a single context to reduce unnecessary re-renders.
  • Memoize Provider Value: Use useMemo to prevent re-rendering all consumers unnecessarily.

Example: Optimizing Provider

import React, { useState, useMemo } from 'react';
import ThemeContext from './ThemeContext';

function App() {
  const [theme, setTheme] = useState("light");

  const toggleTheme = () => {
setTheme(prev =&gt; (prev === "light" ? "dark" : "light"));
}; const value = useMemo(() => ({ theme, toggleTheme }), [theme]); return (
&lt;ThemeContext.Provider value={value}&gt;
  {/* child components */}
&lt;/ThemeContext.Provider&gt;
); }

Explanation:

  • useMemo ensures the provider value object is recreated only when theme changes, reducing unnecessary re-renders of consumers.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *