Using Redux Thunk to Clean Up Messy Components

OpenShift Ninja
Apr 29 · 8 min read
Image for post
Image for post
Image Courtesy of Max Pixel

People like Spaghetti, not Spaghetti Code

If you are like me, you don’t like code that starts getting messy and makes a simple component into a maintenance nightmare. Recently our team has been going through an evolution in how we build our components, and it has resulted in our application becoming much easier to maintain and more adaptable to changing requirements. React Component files are now largely about reading state and building a view, and firing off actions. That’s pretty much it.

React is Great, But You Still Have Work To Do

React applications in general are UI intensive. Our application is full stack, with a Spring/Java backend talking to a database, and a NodeJS web layer that is providing the responses to user requests. Most of my development career, I’ve built these type of applications with most of the business logic in the back and the UI doing very little other than making requests to the backend when data needs to be fetched for a new page or data is being persisted.

Redux Thunk to the Rescue!

So how does Redux Thunk make this better? It makes it better by breaking up all that logic into a series of actions. I don’t mean events like onClick, onMouseUp, etc. I am talking about actual actions in your application. Actions that sound more like how business people would describe them. For example, imagine you had to describe to your business executive how your application validates a form that a customer enters. You would probably make a list of things it does that looks like this:

  • User clicks Next
  • First Name is validated to be a valid name (according to a Regular Expression)
  • Last Name is validated to be a valid name
  • Address is validated against our address validation service
  • Validation is complete, Application moves user to next page

Actions Can Be Done In Parallel

Before you raise your hand and say “Jeffrey, you know that JavaScript is single-threaded, right?”, let me just point out that Promises allow JavaScript to use the idle time waiting on one response to process other instructions, so it is kind of like it is doing things in parallel, even though it really is always executing instructions serially. That’s what I mean by parallel here.

Actions Do the Logic, Reducers Update the State

Anyone who has been using Redux knows that there are really two parts to it — the actions that do some work, and the reducers that update the state. For simple payload actions — which is what a lot of us use Redux for — the action is simply building an object that is passed to the dispatch method and is processed by the reducer:

const setCount = () => ({ type: SET_COUNT, payload: 35 });
...
dispatch(setCount());
const loginUser = (username, pass) => ((dispatch) => {
dispatch(userLoggingIn());
axios.onPost('/api/login', { username, pass }).then((resp) => {
if (resp.loginSuccessful) {
dispatch(userLoginSuccessful());
} else {
dispatch(userLoginFailure(resp.error));
}
}).catch((exception) => dispatch(loginServiceError(exception));
};
const customerReducer = (state = initialState, action) => {
switch(action.type) {
case USER_LOGGING_IN:
return {
...state,
loggingIn: true,
}
case USER_LOGIN_SUCCESSFUL:
...

Components and useEffect

We are almost done here, because really the bulk of the work to use Redux Thunk is just to move a lot of that business logic out of your components and to start building logical business flows. Because you can fire off other actions and keep going, your business logic tends to get a lot simpler. If you are using class components, those components will probably use the Redux store object (or props if wrapping your component with connect). We prefer to use functional components along with hooks so that we can read our state like this:

const LoginComponent = () => {
const { loggingIn, loginSuccessful } = useSelector((state) => ({
loggingIn: state.customer.loggingIn,
loginSuccessful: state.customer.loginSuccessful,
}));
...
useEffect(() => {
if (loginSuccessful) {
setLoginModalActive();
}
}, [loginSuccessful]);

Redux Tools

One last point worth mentioning is that if you use Redux, you should add the Redux Tools plugin to your browser. It will let you see the actions that get fired off, view the state, and even go backward and forward in time in your application so you can see how the state evolved. When moving to Redux Thunk, this becomes even more helpful because now you can see the steps that are going on behind the scenes without using the debugger.

Conclusion

React is great for building UI applications, but you can’t eliminate the need to build business logic that can get complicated. Using Redux Thunk, you can move that business logic to meaningful flows so that the component only needs to fire off the actions. These actions can fire off other actions and then your reducers can be really simple as they update the state. Your components will be able to listen to the state and render results as soon as the state updates. I will dive deeper into this with some examples in an upcoming article.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store