Best practices to use with Redux

Sharma'arke Ahmed
6 min readJul 4, 2020

--

If you’re new to Redux, you’re probably going through a couple of courses and tutorials showing you how to set up and use Redux. But a lot of the time, newbies tend to use Redux in an — let’s just say — unfashionable manner.

Imagine my disappointment

In this article, I’ll be showing you how you can clean up your Redux code and keep things nice and simple.

Action creators

Actions in Redux tends to be the epicentre of your spaghetti code. Most newbies, including myself when I was first learning it, would not separate API calls from action creators. Exhibit A:

In a situation like this, your function is doing two things rather than one. There’s a principle in software engineering called Single-responsibility. In Redux, you must restrict your action creators to do only one thing. This helps with reducing repeated code and also makes the codebase more readable. A better way of doing it would look like this:

Your code now looks more modular and the other person studying your code will have an easier time digesting what your action creators are doing. This also opens up the opportunity to re-use the action creator that would be dispatched in other asynchronous API functions that you may implement.

Action types

This one is a lot more straightforward than the one before, and heck, you should be doing it regardless of whether you read this article or not! But, we’ll cover it anyway (just in case you aren’t doing it 😒).

In Redux, your state is updated based on the action type you provide. The logic is usually handled by your reducer. If I want to update my list of meals, I would call an action creator called fetchMeal(). Here’s Exhibit B:

Notice the problem? If you look at line 4 and line 12, you’ll notice that it is a string literal and not a constant variable. When you dispatch the action creator fetchMeal, I’m passing an object called ‘action’ to all my reducers, which contains a property of type and whatever other properties you add. Your reducers will then execute an if statement (or switch statement, you should be using that) and will check what the value of ‘action.type’ could be. It is paramount that the action type you passed in to be the same as the actions you are trying to check for in your reducer. For example:

if ('fetch_meals' === 'fetch_meals'){
//do something
}

This will mean the block of code inside the if statement will execute if the condition is true. Basic stuff right? Nothing to be worried about then. Unless your codebase starts to expand and you have to deal with loads of action creators and its corresponding reducers. Usually writing your actions and reducers like this would definitely make debugging harder. Your code may be in order and working but the reducer will not recognize the action type you are passing in if there is a slight typo. You’ll just end up with the same state as before, as your reducer will by default return the current state when no conditions are true.

To fix this, it is pretty simple really. In your action folder, you’d create another file called types and in this file, you will be exporting each of your action types as a constant variable:

You can now import these constants into your actions file as well as your reducers.

You’ve now reduced your chances of spending countless hours debugging a typo. One constant of the same value that can be accessed by your actions and reducers.

Initial state

If you don’t know what it is, the initial state is the starting data that your Redux application will display when your app is first started. When first designing and developing your app, you must take into account the structure of your data. This will help define ways to separate your data as well as how they are updated.

When dealing with a large app, you need to know what your list of reducers will be. Let’s study what your state tree could look like:

Right off the bat, we can see that our app would need three reducers. ‘currentFilter’, ‘filter’ and ‘meals’. When you create your reducers and combine them all together using the ‘combineReducers’ method, your state would look something like the above. You’ve probably defined a couple of reducers and combined them together and this is how it would look like. The top-level properties on the state object are the names of your reducers. All the properties underneath them (or value, depending on how you set up some of your reducers) are the reducer’s own state. Whenever you want to update certain parts of your app, you dispatch a certain action that only certain reducers will understand and only those reducers will be updated. This will alleviate the need to update an entire state tree.

Now how would you go about defining your initial state? Can’t really answer that question for you, depends on your application’s needs. For example, if your application fetches data from an API, you’d want to create a separate reducer just for that API call alone. You can add a number of properties to that reducer such as loading flag and success or failure flags, depending on your API’s response.

Where should you define your initial state? There are two ways you can define your initial state, either on each reducer or you pass your entire state tree like the one above to your ‘createStore’ method.

Passing your entire state tree to your store method is particularly useful when you want to populate your tree with preloaded data, either from your server or a previous user session (good for when you have logged in users).

Setting up your initial state on the reducer level is also useful when you just want your reducers to return some starter data. The two are not mutually exclusive and can be used together.

Honourable mentions

  1. Use switch statements

It is not necessary but using switch statements can make your reducers look cleaner. It also makes it easier for you to add new actions should you decide to in the future.

2. Use ‘bindActionCreators’

This is a very handy method you can use to automatically wrap all your actions with redux’s dispatch method. You can read this article on its benefits and how to use it.

3. Separate your redux set up from your index.js

In a large application, you may be bound to add lots of middleware and other configurations to your redux. Your entry file (index.js) will eventually become cluttered with so many config objects and imports of libraries. So, to keep things neat, you can create a separate file just for importing libraries and config objects and return the store object. You can look at an example of how it is done here.

Summary

We’ve looked at some of the ways a redux application can be refactored. We’ve looked at some common pitfalls that usually occurs when building a redux application for the first time and we’ve also looked at some examples to try and get a better understanding of how Redux works.

I hope I have taught you something useful in this article. Redux can be daunting at first with all the different parts you need to understand to make a redux application work, and I too struggled for a good 4 months building React Native apps with it. But hey, you’ll get the hang of it eventually, after all, I am sitting here writing this article, aren’t I?

Be sure to hit that clap button to show some support and connect with me on LinkedIn!

--

--

Sharma'arke Ahmed

I’m just a full-stack dev trying to get his life together