useContext – React

In React, the useContext hook allows you to access values from React Context in a functional component. React Context provides a way to share values (such as global data or functions) between components without passing props down manually at every level.

This is especially useful when you need to pass data deeply down the component tree, avoiding “prop drilling.”

How React Context Works

  • React.createContext(): Creates a context object.
  • Context.Provider: Wraps around components that need access to the context and provides the value to its children.
  • useContext: A hook that allows components to consume the context value directly.

Basic Syntax of useContext

  1. Create a context using React.createContext.
  2. Wrap components with Context.Provider and provide the value.
  3. Use the useContext hook to access the context value in a component.
const MyContext = React.createContext();

function MyComponent() {
  const value = useContext(MyContext);
  return <div>{value}</div>;
}

Step-by-Step Example

Let’s explore a simple example where we have an application theme (like dark or light mode) that needs to be shared between multiple components.

1. Create a Context

import React, { createContext } from 'react';

// Create a Context for the theme
const ThemeContext = createContext('light'); // Default value is 'light'

export default ThemeContext;

2. Provide the Context in a Parent Component

We use the Context.Provider to supply the value (theme in this case) to the children components.

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

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

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

  return (
    <ThemeContext.Provider value={theme}>
      <button onClick={toggleTheme}>Toggle Theme</button>
      <ChildComponent />
    </ThemeContext.Provider>
  );
}

export default App;
  • Here, ThemeContext.Provider wraps around ChildComponent to provide the current theme value to the subtree.
  • The toggleTheme function allows us to change the theme between ‘light’ and ‘dark’.

3. Consume the Context Using useContext

In any descendant component, you can consume the theme value using the useContext hook.

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

function ChildComponent() {
  const theme = useContext(ThemeContext); // Consume the theme context
  return (
    <div style={{ backgroundColor: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
      <p>The current theme is {theme}</p>
    </div>
  );
}

export default ChildComponent;
  • useContext(ThemeContext) allows ChildComponent to access the current theme value.
  • The background and text color change based on the current theme.

Full Example

Here’s the full example of using useContext:

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

const ThemeContext = createContext('light');
export default ThemeContext;

// App.js
import React, { useState } from 'react';
import ThemeContext from './ThemeContext';
import ChildComponent from './ChildComponent';

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

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

  return (
    <ThemeContext.Provider value={theme}>
      <button onClick={toggleTheme}>Toggle Theme</button>
      <ChildComponent />
    </ThemeContext.Provider>
  );
}

export default App;

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

function ChildComponent() {
  const theme = useContext(ThemeContext);
  return (
    <div style={{ backgroundColor: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
      <p>The current theme is {theme}</p>
    </div>
  );
}

export default ChildComponent;

Example 2: Nested Context Usage

Context is particularly useful when data needs to be passed deeply through nested components. Here’s an example with user authentication:

1. Create a User Context

const UserContext = createContext();

2. Provide the Context in a Higher-Level Component

function App() {
  const user = { name: 'John Doe', loggedIn: true };

  return (
    <UserContext.Provider value={user}>
      <Profile />
    </UserContext.Provider>
  );
}

3. Access Context in a Nested Component

function Profile() {
  return (
    <div>
      <ProfileDetails />
    </div>
  );
}

function ProfileDetails() {
  const user = useContext(UserContext);
  return (
    <div>
      <h1>Profile</h1>
      <p>Name: {user.name}</p>
      <p>Status: {user.loggedIn ? 'Logged In' : 'Logged Out'}</p>
    </div>
  );
}

In this example:

  • The user object is passed from UserContext.Provider to deeply nested components, allowing ProfileDetails to access it without intermediate components needing to know about it.

Benefits of useContext

  1. Avoids Prop Drilling: Context eliminates the need to pass props through every level of the component tree. Instead, data is provided at a higher level and accessed directly at deeper levels.
  2. Simplifies Global State Management: Context is useful for small-scale global state management, like theme, language, or authentication status.
  3. Easier Component Maintenance: As components are decoupled from their parents, maintenance becomes easier because changes to the parent do not affect the intermediate components.

When Not to Use useContext

  • Frequent Updates: If the context value changes frequently, all components using that context will re-render. In such cases, other state management tools (like Redux or Recoil) may offer better performance.
  • Overuse: Overusing context for every piece of state can make components harder to maintain. Use it only for global/shared state.

Example of Combined useReducer and useContext

Often, you’ll see useReducer and useContext combined for global state management:

import React, { useReducer, createContext, useContext } from 'react';

const CountContext = createContext();

function countReducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function App() {
  const [state, dispatch] = useReducer(countReducer, { count: 0 });

  return (
    <CountContext.Provider value={{ state, dispatch }}>
      <Counter />
    </CountContext.Provider>
  );
}

function Counter() {
  const { state, dispatch } = useContext(CountContext);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
}

export default App;

Conclusion

  • useContext is a powerful tool for sharing state globally between components without the hassle of prop drilling.
  • It simplifies managing global or shared state, such as themes or authentication, but should be used wisely for performance reasons.
  • For more complex state, useContext can be combined with useReducer.
Tags: No tags

Add a Comment

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