Algobook
- The developer's handbook
mode-switch
back-button
Buy Me A Coffee
Mon Mar 20 2023

How to use context in a React application

What is context in React?

As React developers, we are all familiar with passing props through components. Basically, this is used when you want the child component to get some information from the parent. Like, if you have a component which needs a label for example. Then the props passing would look something like this:

const ParentComponent = () => <ChildComponent label="Label from the parent" />; const ChildComponent = ({ label }) => <div>{label}</div>;

So this is how we normally use props in React. However, if you have a large tree of components, passing props can be very cumbersome and messy in the long run.

If you imagine you have an application, which are using a global state, like night/light mode with a toggle button - imagine the hassle with passing around the value to all UI components for them to display the correct UI.

Creating a context

Let's start with create our ThemeContext.jsx file and create the context.

import { createContext } from "react"; export const ThemeContext = createContext(null);

Now we have created our context, and given it null as default value.

Creating context provider

Next up, we need to extend our ThemeContext.jsx with a provider.

import { createContext, useReducer } from "react"; export const ThemeContext = createContext(null); export const ThemeContextProvider = ({ children }) => { const [isNightMode, setNightMode] = useReducer( (prevState, isNightMode) => isNightMode, false ); const dispatch = (isNightMode) => setNightMode(isNightMode); return ( <ThemeContext.Provider value={{ isNightMode, dispatch }}> {children} </ThemeContext.Provider> ); };

So, now we have extended the context with a provider component that will wrap the part of the application that should make use of the state in the context. We first create the reducer and give it its default value, in this example we say that night mode should be false. We also declare a dispatch function, which will handle all the updates from the consumers.

Wrapping the application

In order for our components to listen to the state changes of the context, we need to wrap it with the context. In the root of the application, wrap the application like this:

root.render( <React.StrictMode> <ThemeContextProvider> <App /> </ThemeContextProvider> </React.StrictMode> );

All right. Now we are set and ready to use the context.

Use the context in a component

In all components that are wrapped under the <ThemeContextProvider> will gain access to the isNightMode value, and we access it like this:

import { ThemeContext } from "contexts/ThemContext"; const { isNightMode } = useContext(ThemeContext);

And if we want to dispatch a new value, we can gain access to the dispatch function and update the theme like this:

const { isNightMode, dispatch } = useContext(ThemeContext); dispatch(true);

And that is how we change the value of the isNightMode value.

How to use context with API calls?

To extend the functionality in our context, we will now implement an API call and add a loading state and error handling.

Changing the provider

We will first change our reducer to be able to handle an object with data instead of just a boolean value. We will have three values, loading for the loading state, data for the actual data being returned from the API and error if any error occurs.

const reducer = (prevState, updatedProperty) => ({ ...prevState, ...updatedProperty, }); const [employeeState, setEmployeeState] = useReducer(reducer, { loading: null, error: null, data: null, });

Change the dispatch

In order to make the call to the API, we need to update our dispatch. We will use a online dummy API: https://dummy.restapiexample.com in this demonstration.

// We are using axios for fetching data import axios from "axios"; const dispatch = async (employeeId) => { // On dispatch, we will set loading to true and reset any errors setEmployeeState({ loading: true, error: null, }); try { // Make the actual call const response = await axios.get(`https://dummy.restapiexample.com/api/v1/employee/${employeeId}`); // Setting loading to false, and setting the data object to the API response setEmployeeState({ loading: false, error: null, data: response.data?.data, }); } catch (err) { // If any errors, we are setting the error object here setEmployeeState({ loading: false, error: err, }); } }; return ( <ThemeContext.Provider value={{ employeeState, dispatch }}> {children} </ThemeContext.Provider> );

Accessing data and dispatching

const { employeeState, dispatch } = useContext(ThemeContext); // Dispatching on component mount useEffect(() => { dispatch("1"); }, []); // Example of displaying data const { data, loading, error } = employeeState; // Returning loading state if (employeeState.loading) { return <div>Loading...</div>; } // Returning any errors if (employeeState.error) { return <div>{error}</div>; } // Returning employee data from API return <div>{data.employee_name}</div>;

That's it. Now we have a context that are fetching data from an API, and handles loading state and errors as well.

To read more about context in React, they have an excellent guide on their dev page. Read more here.

signatureMon Mar 20 2023
See all our articles