State Management in Pure React: Thunks

In the previous post, we discuss about managing state in React's class component, Hooks, and a data fetching strategy. In this last part of the series, we are going to explore a Thunk and recap the series about implementing state management in pure React.


One of the things that we get in Redux is middleware, because reducers. The reducer itself does not have an idea of asynchronous. The useFetch is the one that doing all fetch and dispatch in a number of actions. And so in Redux, we have a way to solve this using some middleware. And one of the really common ways to do that is called Thunk.

What is Thunk?

It's a very complicated way of saying a very simple thing. In normal reducers, we dispatch actions. Redux-thunk or in our case our own custom thunk that we're gonna write, is a function returned from another function.

function definitelyNotAThunk() {
	return function aThunk() {
		console.log('Hello, I am a thunk.')
	}
}

Thunk can be useful to use if code needs to be executed later since it gives a function that will dispatch an action once it receives results from an API. The major idea behind a thunk is that it is code to be executed later, so it allows us to think about this asynchronous code.

useReducer doesn't have an idea of middleware, however, we could theoretically handle this the way that we've been handling other stuff, which is make our own hooks.

const useThunkReducer = (reducer, initialState) => {
  const [state, dispatch] = useReducer(reducer, initialState);

  const enhancedDispatch = useCallback(
    action => {
      if (typeof action === 'function') {
        console.log('It is a thunk');
        action(dispatch);
      } else {
        dispatch(action);
      }
    },
    [dispatch],
  );

  return [state, enhancedDispatch];
};

With useThunkReducer we're gonna separate the asynchronous parts of our application from the synchronous state management pieces, thus in my argument it will make a more modular code.

This way, before we were doing it when it mounted, here now have it as a function that will go ahead and trigger it on an event. That's basically a way to add extra functionality to the dispatch if the very simple useReducer that React doesn't give you what you want, "If dispatch hasn't change, don't give me a new enhanced dispatch every time render is called, just give me back the same one because it's just calling the same dispatch".

Should you find yourself adding lots and lots of different types of middleware? you could, and it's a probably a sign that Redux might be a choice for you.

Wrapping Up

The whole idea of hooks is that you can create abstractions to solve some generic things. And hopefully this pattern can help solve any other special snowflake problems that you have.

But for a lot of common things, I would encourage you to do some research first: has this been done already? is there a solution that I can use?

In the real world, the way we structure a state and what tools we go to grab for use is great. But sometimes useReducer gives us a little more fine tuning as a separate set state management from the rest of out user interface. With that it becomes easier to test, because the useReducer is just javascript objects that we don't really have to think about too much. And because it's just a javascript object, it becomes easier to test.

Takes your business logic, takes a bunch of rules by components, puts it onto the page. If you start to separate states out from the UI, if you start to come up which just managing the data structures outside in a reducer — that's just a plain old javascript function, and let that feed into our application in a very declarative way, you can basically say, "Okay, the state of the world has changed. React, your components are just a set of rules for how that should render."

And you can get to that point if you separate these pieces out, and you can also ratchet it together. Anything that you've done squarely once, you can put into its own little hooks. you never have to see it again, hide your problem. and also reuse the good things.

Thinking about how we structure states, has big impact on the performance. And depending on which trade offs you wanna make, but for the sake of maintainability. This is the tradeoff that we have to think about, and this is the tradeoff that we need to make.

Share article