What the THUNK? Using and understanding Thunk in your React/Redux app.

Ryan Bollettino
4 min readJan 2, 2022

When I was just a wee programmer 14 months ago, making some Ruby code work correctly made me feel like king of the world.

me: 14 months ago (mentally)

It’s laughable to think how much was to come down the pike and the challenges I’d face, (and will continue to down the line!). One such challenge was wrapping my head (and my app) around the concept of middleware — specifically Thunk — and why it’s essential when creating any React/Redux app that requires an API.

First, let’s follow the path for a usual call using Redux:

User will dispatch an action object >
dispatch(addToCollection(item))

That specific action.type triggers its reducer >

export const addToCollection = (item) => ({type: ADD_COLLECT, payload: item})

and regardless of the action, a new version of the state object is created and returned with an updated payload to give us the “current” state.

export const itemReducer = (state = INITIAL_STATE, action) => {
switch(action.type) {
case ADD_COLLECT:
return {...state, myCollection:
[...state.myCollection, action.payload]}

There should be no issues with pulling or adding information from local state, as these are all synchronous actions.

But what happens when you need to use the asynchronous fetch() ? It breaks. Why? Action creators can only return JS objects with a type: (and sometimes payload:) property. So the action is getting sent to the reducer before it has a chance to grab the data from the API (or localhost).

We want the Thunk. We gotta have that Thunk, now.

So there is no way to dispatch synchronous actions when you have an async fetch that needs to happen. This is where the beauty of middleware comes in. Middleware allows us to return an object — OR A FUNCTION!! — within an action creator.

Enter Thunk.

Thunk says, “Hey, you’re gonna wait for a sec while I hit the API endpoint, get what I need, and then we can continue running the action creator function. Ya dig?”

Installing Thunk :
npm install redux-thunk or yarn add redux-thunk

Then, wherever you are defining your store:
import thunk from ‘redux-thunk’
import { createStore, applyMiddleware} from ‘redux’

and define your store:
const store = createStore(rootReducer, applyMiddleware(thunk))

Let’s break it down. I have a collection of Games I want to render. The Games are stored on a localhost on my server. In my App.js, I imported the useDispatch and useEffect hook, and on initial load will run my fetchGames() function.

fetchGames is located within my Actions folder, and here is where we can see the power of Thunk reveal itself. The (dispatch) argument in the top line is my Thunk function, which I named dispatch, since it is already defined. (You may choose to create any function you like and implement that inside of your action creator.)

Remember, Thunk is a middleware function that allows the user to return another function inside of an action creator, in addition to the action object. Thunk intercepts the action before it reaches the reducer, and allows you to run any other functions you need to. In other words, I’m saying here, run the fetchGames action creator, and stop off at the local API and fetch all my Games objects. Once you have those, now I am manually instructing the action creator to dispatch the action object to the reducer, with our new shiny data in tow.

Inside my reducer, it hears the LOAD_GAMES event, and returns an updated store object with the allGames payload included.

now to render all my Games…..
voila

big thanks to Joey Castronovo and Sanjeev Thiyagarajan for their contributions in helping me understand this better.

--

--

Ryan Bollettino

Current Software Engineer student at Flatiron School, 11 year Math Teacher, 20 year thespian.