2020 October 12

React Context with Hooks


I'm having a lot of fun with React. As much as I appreciate the structure class components provide, it does however take a lot of effort to write out and can get complex very quickly if not careful. Using function components have been a breeze, especially for something simple. The reason why I bring this up is that implementing a shared state, or rather context, with other components is super simple. Here's how I've been implementing this.

Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language.

React

CreateContext

Now it's good to separate all of this out, especially when a project grows. In another file I'm going to create a new file called 'ThemeContext' in a new folder called 'contexts' that will be a sibling to the components folder.

  • components/
    • ...our components
  • contexts/
    • ThemeContext.js

First thing to import is createContext and useReducer. The first one is going to become a wrapper for all of the components that will rely on this. The other is where all of the logic is done. So you can kind of think of 'ThemeContext.js' an organized list of declared initial state and functions that will be passed down to child components.

Now this next part, you have a choice. You can either create a new file called 'ThemeReducer' and import this into 'ThemeContext' (what I prefer), or you and write this out next. The pattern I see is contolled by a switch/case.

Up next we have our provider. Instead of using ThemeContext.Provider and ThemeContext.Consumer, we're going to work with useContext later.

To summarize:

  1. We created a new context called ThemeContext

  2. We're including the following:

    • An initial state
    • A function as our reducer
    • A function as our provider
  3. We only export ThemeContext & export default ThemeContextProvider. Exclude the reducer function.

Context Provider

Let's look at contextValues. This is everything that will be handed to your components.

So we take our functions inside the provider and spread the state so theme is accessible. All we have to do is use {theme} in our component and call toggleTheme() as if they were written in the component itself. But how?

Remember what I said that the provider is going to be a wrapper? With context, you'll want to wrap this with your components from the lowest parent component possible, meaning wherever the first component that needs this gets it which isolates this from any others that don't. But since this is a very basic example. We'll just import ThemeContextProvider in index.js for now since it's likely to be used by all.

Context Consumer

Now let's go to our App.js file. Here's where we import our context and destructure to get theme from this. In our initial state we set it as 'light'.

Calling a function to Use the Reducer

Alright so let's do the same thing but let's go to our ExampleUseContext component now. Import ThemeContext and destructure.

Alright so this now will work. Pretty simple, and more organized! But how does this work? So we gave this button the toggleTheme function to call. Basically all this does is pass in a command that's a string 'TOGGLE' that will match a case in our reducer function. From there theme gets updated and then updates the className in our App component. But what if you need to send some data over. Easy!

And then in the button.

Then in the reducer function, it will be available as action.payload where you would handle the logic.

One thing I recommend is that your reducer can get cluttered so I would instead call a function that does the logic. That way everything stays readable!

Copyright © 2021. Jake Wantulok