Category Archives: Context API

Using React Context Hooks For Data-Heavy Apps

I build a lot of mini data-heavy React apps, so I wanted to share my basic architecture. The apps I work on typically have a small number of different REST API calls usually with several parameters that can be changed to query the data set. This might not work for everyone, and I’m not sure how well this scales to bigger applications, but I like how this works for my needs.

With the addition of hooks, functional components in React became a lot easier to write. This is my preferred method of creating React apps at the moment, because it simplifies the code a lot [particularly with the this.handler = this.handler.bind(this) code mess in the constructor] Coming from React Classes, I had to change my basic architecture a little bit, but I’m happy with what I arrived at.

What I’m going to make here is a quick app that has

  • One data API call
  • One Control component
  • One Presentation component
  • The app will display the current time when a time zone button is pressed.

React Context Hooks Time App


GitHub

Here’s an overview of component tree of this architecture.

  • The App just wraps and organizes the app. There can be more there, but we are keeping it simple here.
  • The Controls component will have two choices for time zone. Clicking a different button will change the parameter and make another REST API call.
  • The Presentation component for me typically contains a data grid or other items which present the return from the API. Here it’s just text with data from our time API.
  • The DataContainer component is where this gets interesting. I’m housing all the API fetching, state and control handlers in this component, so we don’t burden the App component with this.

App.js

const App = () => 
        
        
    

This is straight-forward and clean.

Controls.js

import React, {useState, useContext, useEffect} from 'react';

import {ContextItem} from '@src/context';

const Controls = () => {

    const {appParam, methods} = useContext(ContextItem);

    return 
}

The Controls component has two buttons that fire off a method on a click and use our app’s parameters to provide control feedback. This line: const {appParam, methods} = useContext(ContextItem); is the context hook and it’s the key to making this work. I like that this is almost the same bringing props into your class component.

I’m also sending methods along in the context so that we can manipulate the app’s state when the users press the button.

It should be noted that the import statements are necessary for these types of components particularly when we what to use a context. They are also omitted in subsequent component snippets for brevity.

Presenation.js

const Presentation = () => {
    const {data} = useContext(ContextItem);

    return 
The current time for {data.timezone} is: {data.datetime.slice(0, 16)}
}

Same idea here, as the Controls component. We are using the data from the app’s state which we retrieve through the context API to populate the text with some information.

Data Container

const DataContainer = ({children}) => {

    const [data, setData] = useState({datetime: ''});
    const [appParam, setAppParam] = useState({tz: 'UTC'});

   
    // HANDLES DATA
    const getData = () => {
        fetch(`https://worldtimeapi.org/api/timezone/${appParam.tz}`).then(res => res.json().then(json => setData(json))) 
    }

    useEffect(() => {
        getData(); // This hooks gets the data once after the component mounts
    }, [appParam.tz]) // this will run again when the tz prop changes

    // HANDLES EVENTS / STATE MANIPULATION
    const handleAppParamChange = (obj) => {

        const newAppParam = Object.assign({}, appParam, obj);

        setAppParam(newAppParam);
    }


    return 
        {children}
    
}

Here’s the fun part. I use the Data Container as a wrapper, so that the App component has the component layout of the application in it. This gets useful with larger apps, when you want to see multiple data tables or control components.

This component has four parts:

  • The useState() hooks
  • The REST API data fetching
  • The event handler methods
  • The Provider and render return

The useState() hooks are how you define your state structure. This can get more complex, but the nice things about hooks is that you can have different state variables for each important element. In this I’ve broken it up into two parts: data and parameters.

The REST API data fetching handles the promises and fetch calls along with when to run it. Previously in React classes, you would have used the componetDidMount() life cycle method, but with React Hooks we use the the useEffect() hook. The array used as the second parameter in useEffect() determines when that effect will fire again. I put the time zone parameter in there so that it will fire whenever there’s a new time zone to retrieve a new time. [This is a lot cleaner than before.]

The event handler is pretty straight forward. I take the obj from the controls to update the current appParam state value. Then update that state value. Once that updates the useEffect() sees the value for timezone has changed and it fires again.

The last part of the component is the return part. This creates the Provider, puts the state and methods that manipulate that state into the context. The component is made into a wrapper by include the children props in the middle of the Provider.

Conclusion and Benefits

Now I can’t speak for any performance gains or hinderance, but from a code readability stand point I find this approach:

  • Organizes API calls
  • Allows you make subsequent API calls based on parts of state that changes. Eliminating awkward asynchronous issues involved with updating state and using that state to make an updated API call.
  • Prevents props drilling by allowing you to access any part of your context as easily as you would props.
  • Provides a clean organization of components in your app. The Controls and Presentation component don’t have repetitive and messy props to worry about.

React Context API: Multiple Consumer and Providers

With React’s Context API you are able to use multiple Providers and Consumers using different context instances. This could be useful if you want to separate different data models into distinct parts of your app. For example, you could have a data context and a table context. One context instance could be used to control what data your REST API is sending back, while another instance controls your sorting, paging, etc.

Continuing the extremely simple light bulb example, I’ve decided to build multiple rooms (a living room and a hallway) with some switch wall plates and a light in each room. This should be a familiar setup to anyone who has a three-way switch in their hallway, so you can turn the hallway light off at either end of it. In order to make this, we will create:

  • “Circuits” (context instances) for the living room and the hallway
  • Wall plate components for each room to house light switch components
  • A three-way switch for the hallway light
  • A normal switch for the living room light
  • Light bulbs for each room

Putting that together we get the mini-app below:

The full code is available on my GitHub, but let’s start with the core App component:

app.js 📄
class App extends Component {
  constructor(props){
    super(props)
    this.state = {on1: false, on2: false}
  }

  //Room (living room) and Hallway are the two different rooms.
  render() {
    return (
      
this.setState({on1: !this.state.on1 }) } }> this.setState({on2: !this.state.on2 }) } }>
living room
hallway
); } }

There is a lot of component composition here. We have a WallPlate component for each room along with a component for the room itself which houses the light bulb. We have the two providers wrapping the both rooms with the values defined in terms of the App’s state and a method that can set that state. For this example we are using numbered circuits:

Circuits (Context instances)

  • LightCircuit1 — (Living) Room
  • LightCircuit2 — Hallway

The context instances also correspond to a specific variable in the main app components’s state; once again either 1 or 2. In the interest of being straight forward, each context instance have the different states hardcoded into the Provider’s value. If you were using them to control data and the table view separately the Provider’s initial value object should look demonstrably different from each other, but since this is a just a demonstration, they look similar.

The component of most interest is the WallPlateLivingRoom component. It’s the only component which has multiple consumers. [The rest of the components resemble the last few React Context API examples.]

WallPlateLivingRoom.jsx 📄
class WallPlateLivingRoom extends Component {
    constructor(props){
        super(props)
        this.state = {up: false}
      }
    render() {
      return 
{ ({flipSwitch}) => } { ({flipSwitch}) => }
} }

I think the Consumer use is pretty straight forward. You need to first use the correct consumer for what you want to do, then pass the context to your child components as you would with just one component. The React docs show nested Consumer components, but they don’t need to be.

This is a simple example, so using Context API like this is overkill, but you can see how you are able to use different context instances within your app. If you have questions about the basics of Context API or are curious about some of the building blocks of code used in this example check out the previous posts on this topic: A Way Too Simple Context API Example, Context API: Three Way Light Switch.

React Context API: Three Way Light Switch

In the Way Too Simple Context API example, we made a simple light switch. This post will show why flux, the single source of truth, and Context API are really useful.

The last post had just two components (not counting the App component): a light switch and light bulb. Here we are going to add a second light switch making this circuit mimic the three-way switch common in many homes.

In this system we want the following to happen:

  • flipping any switch will change the state of the light
  • the “physical” direction of the switch shouldn’t matter

This is meant to represent a switch in real life, and physical switches don’t throw themselves in response to a change in the system. On a web app, you’d most likely want the component’s feedback to change to reflect the state of the app. If you are curious about how three-way switches work in electrical circuits, it’s quite interesting. Unlike the electric circuit, our mini React app has a single source of truth and by manipulating that we can affect the whole system.

Luckily, because of the way we designed the original Context API light switch app, adding another switch is easy enough as putting a second light switch in the App.js file in the three-way-switch branch of the Context API example project.

app.js 📄
import LightCircuit from './context/context'

class App extends Component {
  constructor(props){
    super(props)
    this.state = {on: false}
  }

  render() {
    return (
       this.setState({on: !this.state.on })
        } }>
        
); } } export default App;

We also added a few aesthetic things: the three-way class to remove the “on” / “off” label and another spacer, but to make this work, all that was added was just another LightSwitch component!

The magic is in the flipSwitch: () => this.setState({on: !this.state.on }) function in the Provider that gets passed to the LightSwitch component and run when someone clicks on the component. The flipSwitch function also looks at the App component’s state for the on property, negates it, and then sets it as the negated value creating a toggling effect.

Once again, since this is a simple example, we could accomplish this by lifting up the state and sending it back down with props, but with Context API it doesn’t need that direct chain. LightSwitch can place it in other components (like a Room, LightPlate, etc.) as long as they are children of the Provider.

A Way Too Simple React Context API Example

React’s Context API is convenient built-in state management for React Projects. It has it’s advantages and disadvantages over a library like Redux for sending props and changing the app’s state. I’m going to focus on the advantages of using Context API and getting an overly simple example to work.

The example in this post only has two levels of components, so it is actually advised against by the React documents. If you were actually making an app with this few of components it would be easier just to pass props and lift up state. But let’s figure out how Context API works! Below is a mini React Context API app that is a light bulb and a light switch.

I’m going to assume a few things:

  • a working knowledge of React.
  • familiarity with the single source of truth behind the Flux model in React
  • familiarity with ES6 JavaScript syntax

Also, the full code for the project is at https://github.com/seandolinar/context-api-example, but the code excerpts in the post are only the relevant parts (I left out the import statements, etc.). I made it with create-react-app, which is a pretty good quick start boilerplate for playing around with React without a lot of setup time.

Alright! There are really just three normal React components:

  1. the App
  2. the LightBulb
  3. the LightSwitch

The app’s global state only has the lightbulb on/off, which affects only the display of the LightBulb. And that state can be changed by only the LightSwitch. Overly simple; let’s go through some code!

context.jsx 📄
export default React.createContext()

Alright, this is too simple. We create a context that can be imported into the different the components.

app.js 📄
import LightCircuit from './context/context'

class App extends Component {
  constructor(props){
    super(props)
    this.state = {on: false}
  }

  render() {
    return (
       this.setState({on: !this.state.on })
        } }>
        
); } }

This object is the heart of the app, so let’s start here. We create a state in the App’s constructor, initially setting the state.on = false. The new part is the LightCircuit.Provider Context API component. It wraps the what would have been the root app component. The value attribute of the Provider is the default/starting value. There are two items in the default context:

  • the App’s state [state]
  • a function for changing the App’s state [flipSwitch()]

This will be passed to any Consumer components like in the LightBulb component.

LightBulb.jsx 📄
import LightCircuit from './context/context'

class LightBulb extends Component {
    render() {
      return 
                {
                    ({state}) => 
}
} }

This component is just a Context Consumer component, and a div that uses the context’s state. The ({state}) => part of the arrow function destructs the context and only uses the state, which is then used to change the className which controls CSS to show the light bulb is lit or dark. The div that’s inside the consumer component could be any component, composition of components, or even JavaScript.

The annoying thing about Context API is having to pass the context through the arrow function. It creates some JavaScript structure I’m not crazy about since it limits what you can do with the context in that component without additional code.

LightSwitch.jsx 📄
import LightCircuit from './context/context'

class LightSwitch extends Component {
    constructor(props){
        super(props)
        this.state = {up: false}
      }
    render() {
      return 
                {
                    ({flipSwitch}) => {
                        const handleClick = () => {
                            this.setState({up: !this.state.up})
                            flipSwitch()
                        }
                        return 
                    }
                }      
            
    }
}

The LightSwitch component does one important thing: it runs flipSwitch() from the context, which in turn changes the state in the top-level App component. We are destructuring the flipSwitch() from the context then putting that into a catch all handelClick() function to run when the button is clicked.

I did add one extra layer on complication, I create an up and down state internal to the switch. Since the circuit doesn’t affect the position of the physical switch in real life, so it shouldn’t here. That is why the extra handleClick() function was made and why the LightSwitch component has its own state.

Conclusion

Hopefully, walking through the simple example with Provider, Consumer and Context can help you get Context API working for your app. Again, this is too simple of a demo, and actually a bad example of how to use it. It works well if the light switch was buried within a hypothetical LightPlate, Wall, Room, and Circuit component. That way you wouldn’t have to lift up state through several layers of components.