DEV Community

Cover image for Project 24 / 100 - Dark and Light Mode CRA with the React Context API
James Hubert
James Hubert

Posted on

Project 24 / 100 - Dark and Light Mode CRA with the React Context API

Hey! I'm on a mission to make 100 React.js projects ending March 8th. Please follow my dev.to profile or my twitter for updates and feel free to reach out if you have questions. Thanks for your support!

Link to today's deployed app: Link
Link to the repo: github

This week I've been studying the Context API. This project used Context to share what is essentially a piece of appwide state- dark or light mode- across the React component tree.

The Context API

Let me take a quick moment to try and describe Context. There are alternatives to Context, and situations where you don't need it at all, but the gist of it is this: if you need a piece of state that can be optionally shared across any component in your React directory, and your React directory has multiple or many levels so that sharing a piece of state down the tree would be cumbersome, Context might be a good tool for you.

The ReactJS documentation describes an example where an avatar and username needs to be passed down multiple levels to a sub-component, like from index.js to App.js to Page.js to Navbar.js to Avatar.js:

"It might feel redundant to pass down the user and avatarSize props through many levels if in the end only the Avatar component really needs it. It’s also annoying that whenever the Avatar component needs more props from the top, you have to add them at all the intermediate levels too."

In situations like this Context is a great tool.

Implementation

In my application we just had a couple of levels in the React tree. The App.js component renders the following components: Navbar and Main. We have no need to display information about the theme in the main App component so it would be a bit annoying to have to pass it down through props, that's why we're using Context to share that information from index.js through App.js to Navbar and Main.

To handle Context we create a new JS file for all of the Context related bits, which are a part of the React package. We then pull out Provider (this produces the context for use in the app) and Consumer (this consumes the context from the producer wherever in the app you need it to).

The Consumer part is easy- that's just a pure React component that we can feed some children later when we import it in other components. It's the Provider part that requires more finesse. It's an actual component that has state (this is where the context is kept).

We give the component state for the piece of context you want to use, then use the Provider component within the component's return. Lastly, Providers have a required prop called value that we use to pass data to the consumer.

import React from 'react';
const { Provider, Consumer } = React.createContext();

class ThemeContextProvider extends React.Component {
  state = {
    theme: 'dark',
  };

  toggleTheme = () => {
    this.setState((prevState) => {
      return {
        theme: prevState.theme === 'light' ? 'dark' : 'light',
      };
    });
  };

  render() {
    return (
      <Provider
        value={{ theme: this.state.theme, toggleTheme: this.toggleTheme }}
      >
        {this.props.children}
      </Provider>
    );
  }
}

export { ThemeContextProvider, Consumer as ThemeContextConsumer };
Enter fullscreen mode Exit fullscreen mode

As you can see from the example, if you want other components to be able to change the app-wide context, you also need to create a component method to change state, and pass that into the value prop object as well.

Consuming Context

Alt Text

A Pacman-looking Consumer consumes some delicious React context

Later on, when we have a component that we want to provide context to, we simply import the Consumer we created earlier and wrap the rest of the component in curly brackets to provide it as children to the Consumer component.

In this example, my toggle is given both the theme context and the function we passed to the Provider to change state called toggleTheme. When the user changes the toggle, we call this method.

import React from 'react';
import './ThemeToggle.css';
import { ThemeContextConsumer } from './themeContext';

function ThemeToggle() {
  return (
    <ThemeContextConsumer>
      {(context) => (
        <div className='toggle'>
          <span className={`toggle-label ${context.theme}-theme-text`}>
            Light Mode
          </span>
          <label className='switch'>
            <input
              type='checkbox'
              checked={context.theme === 'dark'}
              onChange={context.toggleTheme}
            />
            <span className='slider round'></span>
          </label>
          <span className={`toggle-label ${context.theme}-theme-text`}>
            Dark Mode
          </span>
        </div>
      )}
    </ThemeContextConsumer>
  );
}

export default ThemeToggle;
Enter fullscreen mode Exit fullscreen mode

Conclusion

Context is apparently a notoriously difficult thing to grasp so I, once again, highly recommend taking Scrimba's React bootcamp so that the wonderful Bob Ziroll can teach you. I've done my best. The only thing left is to pick the color scheme for the React Day theme. I chose the red color that Frontendmasters.com uses for their header because I like it, and found complementary colors from there. Pretty cool!

Here are the React docs for Context:
Context API React Docs

Top comments (0)