Using React Global State with Hooks and Context

switches

The Goal

Set and manage global state for a small React Native app built from functional components, without using Redux or Mobx. 

Developers may want to use global state when many components need access to the same stateful information, such as the current user’s info or theme settings (light mode or dark mode).

Seems like it should be simple, right? I was surprised at the lack of tutorials covering this challenge – it might be due to the newness of Hooks. The following is the article that I wish I had read when I started my project. It’s aimed at beginner React developers, or those looking for a refresh on using Hooks and Context. I hope you enjoy!

Vocab

If you’re reading this article, you probably are familiar with the following React vocabulary, but in case you need a refresher:

  • Context provides a way to share data between components without having to pass explicitly via props. 
  • Hooks allow you, among other things, to add state to functional components. Hooks let you use most of React’s features without classes.

Overview

We’ll store our global state and the functions to update them in a Context object and we’ll use Hooks to update the global settings stored within that object. 

This article assumes that all functions in the app are functional components. For a look at a similar approach using class components, check out this example from AcadeMind

Note: I’ve found that standardizing on functional components saves a lot of componentDidMount() componentWillMount()  complexity –– for functional components, the approach outlined below just... works... without any headache.  

p.s. Converting simple classes to functional components using Hooks for state management can be pretty straightforward – there are lots of articles online that walk you through it.

Let’s get started.

Creating the context

1. Create a component to hold the context.

// components/AppContext.js
import React from "react";

const AppContext = React.createContext();

export default AppContext;

2. Define global variables

At the top of the component tree, ie in App.js, define global variables, and the Hooks to update their state.

// App.js
import React, { useState } from 'react';
import AppContext from './components/AppContext';

export const App = () => {
  // Define as many global variables as your app needs, and hooks 
  // to set the state of the variable.
  const [setting1value, setSetting1value] = useState('initialValue1');
  const [setting2value, setSetting2value] = useState(false);
// ...

3. Define functions to update the global variables

// App.js continued
// For each global variable, define a function for updating it.
// In the case of setting1value, we’ll just use setSetting1value.

// If we want to toggle setting2value, we can create a function to do so...
const toggleSetting2 = () => {
  setting3 ? setSetting2(true) : setSetting2value(false);
};
// ...

4. Create an object

Create an object to hold the global variables and functions to update them.

// App.js continued
const userSettings = {
  setting1name: setting1value,
  setting2name: setting2value,
  setSetting1value,
  toggleSetting2,
};

5. Wrap the App with a context provider.

Wrap the entire App in `AppContext.Provider` and pass down the `userSettings` object as props.

// App.js continued
return (
  <AppContext.Provider value={userSettings}>
    {/* All other components are wrapped by the AppContext.Provider */}
  </AppContext.Provider>
);

 

Accessing the context

Now, any child component of the AppContext.Provider wrapper has access to both global variables and the functions to update them:

// components/MyComponent.js
import { useContext } from 'react';
import AppContext from './AppContext';

export const myComponent = () => {
  // Get the global variables & functions via context
  const myContext = useContext(AppContext);
   // ... component continues

 

Updating context

Similarly, child components can update global context using the functions passed down in the context. For example:

import React, { useContext } from "react";
import { TextInput } from "react-native";
import AppContext from "./AppContext";

export const MyTextInput = () => {
  // Get the global variables & functions via context
  const myContext = useContext(AppContext);

  return (
    <TextInput
      value={myContext.setting1value}
      onChangeText={myContext.setSetting1value}
    />
  );
};

 

Conclusion

There you have it! A simple and extensible way to manage state for nested child components using React Context and Hooks. 

For a simple settings page for a React or React Native app, Context + Hooks could be the perfect lightweight solution