DEV Community

Cover image for Project 62 of 100 - Simplify Context components with the useContext() Hook in React
James Hubert
James Hubert

Posted on

Project 62 of 100 - Simplify Context components with the useContext() Hook in React

Hey! I'm on a mission to make 100 React.js projects ending May 31st. 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

Way back in December I published this simple project that was all about using the React context API by extending React's pre-build components ((link to project)[https://dev.to/jwhubert91/project-24-100-dark-and-light-mode-cra-with-the-react-context-api-3e4k]).

Today I'm going back to Context and creating something similar. It's a UI theme switcher using context- this time using hooks instead of class components.

useContext()

As a part of the relatively newer React approach of phasing out React class components, we need to learn the React hooks way to extend basic React functionality like Context or state. There is the added advantage that it also looks better.

In this project we have our App component, a Button component and a Header. All we're going to do is switch between light mode and dark mode in your application and React's Context API is a great way to achieve this.

When using the useContext() hook we don't have to extend class based components. Like in project 24 we need to create a pure React component to house our context provider and to instantiate the Context API. We use this component to then export the context and context provider anywhere else in the application.

import React, {useState} from 'react'
const ThemeContext = React.createContext();

function ThemeContextProvider(props) {
  const [theme,setTheme] = useState("dark")

  const toggleTheme = () => {
    setTheme(prevTheme => prevTheme === "light" ? "dark" : "light")
  }

  return (
    <ThemeContext.Provider value={{theme,toggleTheme}}>
      {props.children}
    </ThemeContext.Provider>
  )
}

export {ThemeContextProvider,ThemeContext}
Enter fullscreen mode Exit fullscreen mode

Above we create an instance of React's context then assign it to the variable ThemeContext. We then create state on this component with the useState hook and store a variable theme in state. We also create a method on this functional component called toggleTheme() that switches the state of this component.

As in our class component Context example, we use the Provider property on our instantiated context ThemeContext and use its pre-built value prop to store the theme state variable and toggleTheme method from this functional component.

You can then provide that context to any other part of your application but typically you want it pretty high up in the component hierarchy so that more of your application has access to this context. We put it as high up as it gets in the index.js file. This is the same with our without useContext, since it's just importing the provider and wrapping the application in it.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import {ThemeContextProvider} from './themeContext';

ReactDOM.render(
  <ThemeContextProvider>
    <App />
  </ThemeContextProvider>,
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

Consuming Context with useContext()

Remember that the cool thing about Context is that you really don't need to pass anything down to lower level components with props as long as a provider for that context is positioned higher up the component tree. Since we've done that, our App component doesn't have any mention of context despite the fact that we know we're going to consume data from it lower down in Button and Header.

import React from 'react';
import Header from './Header';
import Button from './Button';

function App() {
  return (
    <div className="App">
      <Header  />
      <Button />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

To actually consumer the context we go directly into the components where we want to use it. Let's start with Header since it's purely a UI change.

import React, {useContext} from "react"
import {ThemeContext} from "./themeContext"

function Header(props) {
    const {theme} = useContext(ThemeContext)
    return (
        <header className={`${theme}-theme`}>
            <h2>{theme === "light" ? "Light" : "Dark"} Theme</h2>
        </header>
    )    
}

export default Header
Enter fullscreen mode Exit fullscreen mode

To consume our theme data from ThemeContext we simply bring in the useContext React hook then store the exported theme in a variable in the functional component, above the return. We import the ThemeContext and then can use useContext() to specify which context we want to use:

const {theme} = useContext(ThemeContext)
Enter fullscreen mode Exit fullscreen mode

Since we've destructured the theme variable out, we can now use it like any other variable in our component. We will use it to send data about which theme (light or dark) we have stored in context for the app and change the className of the header accordingly. In our CSS the two classes have different colors associated with them:

.dark-theme {
  background-color: #333333;
  color: whitesmoke;
}

.light-theme {
  background-color: whitesmoke;
  color: #333333;
}
Enter fullscreen mode Exit fullscreen mode

In our Button component we also consume the theme context but will import a function into the button as well, to toggle the theme.

import React, {useContext} from "react"
import {ThemeContext} from "./themeContext"

function Button(props) {
    const {theme, toggleTheme} = useContext(ThemeContext)
    return (
        <button 
            onClick={toggleTheme} 
            className={`${theme}-theme`}
        >
            Switch Theme
        </button>
    )    
}

export default Button
Enter fullscreen mode Exit fullscreen mode

This syntax is a lot more clean than wrapping the button component in a Consumer component as in my earlier project using context.

The button now toggles the theme throughout the application and we did it by using data from the themeContext functional component and a little useContext(). Pretty easy! I'm definitely loving React Hooks.

If you like projects like this and want to stay up to date with more, check out my Twitter @jwhubert91, I follow back! See you tomorrow for another project, this time on custom hooks.

Top comments (0)