Eliaslog.pw

What is a Redux thunk and why do I need it?

To interact with a given Redux-store we usually invoke an event with a certain action type. Sometimes we might even put some additional data as payload inside of the event.

But what if you want to do some data manipulation or, beware, some async data fetching? This is where thunks in redux come in handy. They are basically ‘super events’ for redux that not only can interact (getState and dispatch action) with the store, but also handle async and sync processes.

How do I do this?

If your are using the Redux toolkit, it’s already installed in your project.

Otherwise we need the middleware (Github).

npm install redux-thunk

And we need to register the middleware like so:

//store.js
import { createStore, applyMiddleware } from "redux"
import thunk from "redux-thunk"
import rootReducer from "./reducers/index"

const store = createStore(rootReducer, applyMiddleware(thunk))

export default store

Creating the thunk

Let’s say we want to call an API that requires userID and returns user data that we want to store in our redux store.

To do this we need a nested function. The outer function provides the parameter and returns the async function that provides the dispatch and getState parameters to interact with the redux store.

The most basic version would look like this:

//api-call.thunk.js

import { getUserData } from "../get-user-data.js"

const apiCallThunk = userID => async (dispatch, getState) => {
  //Some async api call
  const userData = await getUserData(userID)

  //Dispatch redux event
  dispatch({
    type: "set-user-data",
    payload: {
      userData: userData,
    },
  })
}

export default apiCallThunk

The store would look like this:

//store.js

const startingState = null

const reducer = (state = startingState, action) => {
  switch (action.type) {
    case "set-user-data":
      return action.payload.userData

    default:
      return state
  }
}

export const store = createStore(reducer)

Adding checks to the thunk

Within the thunk, we not only can call an async api, we can also check for errors or existing data like so:

//api-call-v2.thunk.js

import { getUserData } from "../get-user-data.js"
import { someTypeOfErrorChecking } from "../error-checking.js"

const apiCallThunk = userID => async (dispatch, getState) => {
  //If we already have a user in store, don't call the api and end the thunk

  if (getState() !== null) return

  //Some async api call
  const userData = await getUserData(userID)

  //If the api call doesn't succeed, don't dispatch the event
  if (someTypeOfErrorChecking(userData)) return

  //Dispatch redux event
  dispatch({
    type: "set-user-data",
    payload: {
      userData: userData,
    },
  })
}

export default apiCallThunk
The creation of this post was made possible by coffee.
Buy me a coffee