MSDN Documentation

React State Management: Context API

Understanding the React Context API

The Context API in React provides a way to pass data through the component tree without having to pass props down manually at every level. It's designed to share data that can be considered "global" for a tree of React components, such as the current authenticated user, theme, or preferred language.

Why Use Context?

Consider a scenario where you have deeply nested components that need access to the same piece of state. Without Context, you would have to "prop drill" this data through every intermediate component, even if those components don't directly use the data. This can make code harder to read, refactor, and maintain.

Context API solves this by allowing you to create a "context" that holds the data, and then any component within that context's provider can subscribe to its changes without needing props passed down.

Core Concepts

The Context API primarily involves three main parts:

Creating and Using Context

Let's walk through an example of creating and using a simple theme context.

1. Create the Context

First, create your context. It's a common practice to define this in a separate file.


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

export const ThemeContext = createContext({
    theme: 'light',
    toggleTheme: () => {}
});

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

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

    return (
        
            {children}
        
    );
};
            

2. Provide the Context

Wrap your application or a relevant part of it with the ThemeProvider. Typically, this is done in your root component (e.g., App.js).


// src/App.js
import React from 'react';
import { ThemeProvider } from './contexts/ThemeContext';
import SomeComponent from './components/SomeComponent';
import './App.css';

function App() {
    return (
        
            

My App

); } export default App;

3. Consume the Context

Components anywhere within the ThemeProvider can now access the context value.

Using useContext Hook (Recommended for functional components)

The useContext hook is the modern and most straightforward way to consume context in functional components.


// src/components/SomeComponent.js
import React, { useContext } from 'react';
import { ThemeContext } from '../contexts/ThemeContext';

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

    return (
        

Current Theme: {theme}

This component is part of the context.

); } export default SomeComponent;
Using Context.Consumer (Older approach, useful for class components)

While useContext is preferred, you might encounter or need to use the Context.Consumer component, especially in class components.


// src/components/AnotherComponent.js
import React from 'react';
import { ThemeContext } from '../contexts/ThemeContext';

class AnotherComponent extends React.Component {
    render() {
        return (
            
                {({ theme, toggleTheme }) => (
                    

In Another Component:

Theme is: {theme}

)}
); } } export default AnotherComponent;

Context in Class Components

For class components, you can use static contextType or Context.Consumer.


// src/components/ClassComponentWithContext.js
import React from 'react';
import { ThemeContext } from '../contexts/ThemeContext';

class ClassComponentWithContext extends React.Component {
    static contextType = ThemeContext; // Specify the context type

    render() {
        const { theme, toggleTheme } = this.context; // Access context via this.context

        return (
            

In a Class Component:

Current Theme: {theme}

); } } export default ClassComponentWithContext;

When to Use Context

Context is best suited for:

When NOT to Use Context

Context is not a replacement for all prop passing. Avoid using it for:

Performance Considerations

When the value in a Provider changes, all consuming components re-render by default. If you have a deeply nested component tree or a context value that changes frequently, this can lead to performance bottlenecks. You can mitigate this by:

  • Splitting contexts into smaller, more specific contexts.
  • Using React.memo or PureComponent to prevent unnecessary re-renders of components that don't need the updated context value.
  • Memoizing the context value itself if it's an object or array to prevent re-renders when the reference doesn't actually change.

Conclusion

The React Context API is a powerful built-in tool for managing global state in your applications. By understanding createContext, Provider, and Consumer (or the useContext hook), you can efficiently share data across your component tree, leading to cleaner and more maintainable code.