How to set up React global state with hooks, not Redux

Photo by Anne Nygård on Unsplash

When I learned React, the bootcamp curriculum featured a blend of class and functional components, hooks were covered in just a single afternoon lecture, and we used Redux for global state management. Ever since starting my post-bootcamp career, I’ve been working pretty minimally in JavaScript, and all in either vanilla JS or jQuery. The React world has also had a lot of exciting changes and certain norms have been codified in the meantime. Everything is functional components now and hooks are the way of the future, but my interest was really piqued by hearing that global state is now possible to implement purely in React — no Redux needed. I love React and want to keep up my skills, so I’ve started building a pretty basic front-end app to get a solid grounding in some more contemporary practices that I didn’t get a chance to dive into in bootcamp. In the interest of helping those coming behind me, I’ve decided to blog about my process and any insights I have along the way.

PLEASE keep in mind that this is a living document being written as I figure this project out. I might not necessarily be following best practices, and could potentially make some mistakes. I (personally) find following along with a practical project a more useful learning tool than lessons or tutorials, which can often get abstract or put the cart before the horse, but it does come with tradeoffs. I’ll try and note where I’m just taking my best guess, and even come back to edit any egregious mistakes if they arise, but be aware this isn’t an expert tutorial (and if you happen to catch any major oopsy-goofs on my part, please contact me!)

Okay with that out of the way, let’s get started!

I love trivia, so I’m making a trivia app using the Open Trivia Database API. My goal is to make a 30-question game of 10 easy, medium, and hard questions apiece where users race against the clock to get as many questions correct in as little time as possible. I might add some bells and whistles down the line, but for now I’m just focusing on that core functionality.

My initial goal is to explore how to implement global state using hooks instead of Redux. Two articles that were of immense help getting up and running were this one by Ebenezer Don of LogRocket and this Medium article by Martin Crabtree. Both implement state slightly differently and I ended up leaning more towards the second for my own project, but both are hugely useful.

Here’s the top level overview of what I need to do for phase one:

  1. Create a basic reducer with its initial state
  2. Create a store that contains global state and make it globally accessible
  3. Make an API call that interacts with global state to ensure everything works at a basic level before building on top of it

Now that we have an outline of our basic steps, let’s get into the actual implementation.

Step 1: Basic reducer + initial state

After running create-react-app, I create a new file called store.js in the src folder. This will be in charge of the global state. For values I'll need to keep in state, I know that I need one to keep track of whether the API is fetching, and an array to keep track of the questions. More values will definitely be necessary down the line, but remember this is just a basic start. As with Redux, the reducer is a pure function. To test my initial setup, I'm aiming to make an API call that retrieves 30 questions and saves them in state, so for now I'm just going to make an action for when the call is dispatched, and an action apiece for when the call returns successfully and unsuccessfully. I'm including console logs for in-browser testing purposes of seeing exactly what I'm getting back.

Step 2: Make state globally accessible

Next I use React’s Context API to create a context object with its initial state set to, appropriately enough, initialState. Then I create a store provider component by creating a function that uses the useReducer hook and returns the context object's Provider component with the reducer hook set as its value.

In order to make state globally accessible, I just need to wrap my app component in the StoreProvider...

… and include the following useContext call within any component that needs to access global state:

Step 3: Test global state with API call

Eventually I’ll need to split the app up into a component hierarchy, but for purposes of testing that everything is wired up correctly I’m just keeping everything in App.js for now. I'm also trying to keep my imported packages to a minimum, but I do like axios for making HTTP requests.

I create a component clickMe that when clicked, fires the function fetchAndDispatchTrivia. This function dispatches "FETCH_TRIVIA_START" to tell global state that the app is in the process of fetching. It then initializes an empty array to store the trivia questions, and chains together three separate calls to the API (for 10 easy, medium, and hard questions), pushing the results of each call to the questions array. If all goes well, the function should dispatch a success action and a 30-member array of trivia questions should be saved to global state. Otherwise it should throw an error. While the app is fetching, the "Click for trivia" text should be replaced with a "please wait" component (based on reading the isFetching value from global state).

Testing in-browser, everything works as expected, so I’m free to move on to more core implementation of the app interface, which I’ll cover in my next blog installment.

Former paralegal gladly opting for programming instead of law school. Engaged in a years-long, steady migration northward.