Self-paced

Explore our extensive collection of courses designed to help you master various subjects and skills. Whether you're a beginner or an advanced learner, there's something here for everyone.

Learn live

Join us for our free workshops, webinars, and other events to learn more about our programs and get started on your journey to becoming a developer.

Learning library

For all the self-taught geeks out there, here is our content library with most of the learning materials we have produced throughout the years.

It makes sense to start learning by reading and watching videos about fundamentals and how things work.

Search from all Lessons

← Back to Lessons
Full Stack Development
Start tutorial

Global state with the Context API

Why was life before the Context API more difficult?

When working with React, sharing data between multiple components can become complicated. Initially, we use useState to handle local states, but as our application grows, we encounter problems such as:

  • Excessive prop drilling: Passing data from a parent component to a distant child component can be frustrating and inefficient.

  • Scattered states: Each component manages its own state, making it difficult to coordinate global changes.

  • Lack of centralized state management: Without a proper structure, updating the state globally can become messy.

The Context API along with useReducer solve these problems

React provides us with the Context API to share data without manually passing props, and useReducer helps us handle state changes in a predictable and structured way.

Key benefits of this combination:

  • A global state that can be accessed from anywhere in the application without the need for props.

  • A centralized mechanism to modify the state through well-defined actions.

  • More organized and maintainable code.

How does the Context API work?

The concept is simple: a single provider shares information with multiple consumers, and useReducer manages the changes in a structured way.

Every time the state changes, the components that consume it are automatically updated. It's similar to a television signal: a channel broadcasts the signal, and all tuned-in TVs receive it.

Everyone now has access to the global context.

Context API Explanation

Step-by-step implementation

Step 1: Define the reducer and the initial state

A reducer is a function that receives the current state and an action, and returns a new state based on that action.

1// store.js - Define the initial state and reducer functions 2 3export const initialStore = () => ({ 4 todos: ["Make the bed", "Take out the trash"] 5}); 6 7export default function storeReducer(state, action) { 8 switch (action.type) { 9 case "ADD_TODO": 10 return { ...state, todos: [...state.todos, action.payload] }; 11 default: 12 return state; 13 } 14}

Step 2: Create the context and the provider

Here we create a Context and a Provider that will wrap our application to share the global state.

1import { useContext, useReducer, createContext } from "react"; 2import storeReducer, { initialStore } from "../store"; 3 4const StoreContext = createContext(); 5 6export function StoreProvider({ children }) { 7 const [store, dispatch] = useReducer(storeReducer, initialStore()); 8 return ( 9 <StoreContext.Provider value={{ store, dispatch }}> 10 {children} 11 </StoreContext.Provider> 12 ); 13} 14 15export default function useGlobalReducer() { 16 return useContext(StoreContext); 17}

Step 3: Wrap our application with the provider

To allow all components to access the global state, we need to wrap our application with the StoreProvider in the main file.

1import React from "react"; 2import ReactDOM from "react-dom"; 3import { StoreProvider } from "./context/StoreContext"; 4import App from "./App"; 5 6ReactDOM.render( 7 <StoreProvider> 8 <App /> 9 </StoreProvider>, 10 document.getElementById("root") 11);

Step 4: Use the context in a component

Now we can access the global state and modify it from any component with useGlobalReducer().

1import React from "react"; 2import useGlobalReducer from "../context/StoreContext"; 3 4const TodoList = () => { 5 const { store, dispatch } = useGlobalReducer(); 6 7 return ( 8 <div> 9 <ul> 10 {store.todos.map((task, i) => ( 11 <li key={i}>{task}</li> 12 ))} 13 </ul> 14 <button onClick={() => dispatch({ type: "ADD_TODO", payload: `Task ${store.todos.length + 1}` })}> 15 + Add task 16 </button> 17 </div> 18 ); 19}; 20 21export default TodoList;

Unidirectional data flow

The store is the central point of our application, so we must ensure that its information is not modified directly. Instead, we use dispatch to execute actions that update the state.

Test the code live

Upcoming workshops