12 Things Not to Do When Building React Apps With Redux

What to avoid based on my experiences working in production

jsmanifest
Jun 9 · 9 min read

When you’re building React applications, small projects can often be a little more flexible than large projects when it comes to code architecture. While there’s nothing really wrong with building a small application following best practices intended for larger applications, it may be unnecessary to apply the big decisions. The smaller the application is, the more it becomes OK to be lazy.

However, the best practices in this article are intended to represent any size React applications.

If you’ve never had experience building an application in production, then this piece will help you prepare for it. The worst thing that could happen to you is building an application at your job, only to realize that you have to refactor large amounts of code to be more scalable and maintainable — especially if you’re missing unit tests!

Trust me. I’ve been there. I’ve been told countless times to complete x by y. At first, I thought everything was going smooth and magnificent. I thought that just because my web application worked and felt fast that I was doing an excellent job developing and maintaining my code. I knew how to use Redux and make the user interface components interact with one another as expected. Reducers and actions were an easy concept for me. I felt invincible.

Until the future crept up.

A couple of months and 15+ features later, things were becoming out of control. My code utilizing Redux was no longer easy to maintain.

“Why?” you may ask.

“Weren’t you invincible?”

Well, I thought so too. It ended up being a ticking time bomb waiting for a disaster to happen. Redux has the amazing ability to keep things maintainable if used correctly in a large-scale project.

Read along to find out what not to do if you’re planning on building scalable React web applications.


1. Placing Actions and Constants Into One Place

You might see some Redux tutorials out there placing constants and all of the actions into one place. However, it can quickly become a hassle as the app gets bigger. Constants should be in a separate location, like ./src/constants, so that there is one place to search and instead of multiple locations.

Anyways, it’s definitely okay to create a separate actions file representing what or how it is going to be used with, encapsulating directly related actions. If you were building a new arcade/RPG game and including the character classes warrior, sorceress, and archer, it will be a lot more maintainable if you placed your actions like this:

src/actions/warrior.js
src/actions/sorceress.js
src/actions/archer.js

Rather than something like:

src/actions/classes.js

If the app ends up becoming huge, it’s probably a better approach to go with something like this:

src/actions/warrior/skills.js
src/actions/sorceress/skills.js
src/actions/archer/skills.js

If this was a real-world scenario, the bigger picture could possibly turn out to be something like this if we were to organize them this way:

src/actions/warrior/skills.js
src/actions/warrior/quests.js
src/actions/warrior/equipping.js
src/actions/sorceress/skills.js
src/actions/sorceress/quests.js
src/actions/sorceress/equipping.js
src/actions/archer/skills.js
src/actions/archer/quests.js
src/actions/archer/equipping.js

An example of how the sorceress actions would look:

src/actions/sorceress/skills

src/actions/sorceress/equipping

The reason why we do this is that there will most likely always be new features to add, and you must prepare for them as your files become more bloated!

It may feel redundant in the beginning but these approaches will begin to shine the more the project gets larger.


2. Placing Reducers Into One Place

When my reducers start looking like this:

There can obviously be a big mess created very quickly, so it’s best to keep your state structure simple and as flattened as possible, or try to compose all of your reducers instead.

A neat trick is to create a higher order reducer that generates reducers, mapping each wrapped reducer to an object mapping from action types to handlers.


3. Naming Your Variables Poorly

Naming your variables sounds like a no-brainer, but it can actually be one of the most difficult things to be good at when writing code.

It’s essentially a clean coding practice and the reason this term even exists is that it's so important to apply in practice. Poorly naming your variables is a good way to let your team members and your future self suffer!

Have you ever tried to edit someone else’s code and ended up having a hard time understanding what the code was trying to do? Have you ever ran someone’s code and it turned out to function differently than you had expected?

I’m willing to bet that the author of the code was applying dirty code practices.

The worst situation to be in is having to go through this in a large application where it’s happening in multiple areas.

Let me give you a real-life experience of a situation that I was in:

When I was given a task to add and show additional information about each doctor in a table list, I began editing an existing React hook from the app code. At the time, when they choose (clicked) on a doctor, the doctor's information was saved into some state so we could send the information on the next API request.

Everything was going fine, except I was spending more time than I should have searching for where that part was in the code.

At this point in my head I was looking for words like info, dataToSend, dataObject, doctorProfile or anything relating to the data that was just gathered. 10–15 minutes later, I found the part that implemented this flow and the object it was placed in was named paymentObject. When I think of payment objects, I think of CVV, last 4 digits, zip code, etc. Out of the 11 properties, only three were related to paying: charge method, payment profile id, and coupons.

And it didn’t help that it was way too awkward trying to blend in my changes afterwards.

In short, try to refrain from naming your functions or variables like this:


4. Changing the Data/Type Structure Mid-Way

One of the biggest mistakes I’ve ever made was changing the data/type structure of something during an already-established flow of the app. The new data structure would have been a huge boost in performance, as it utilized object lookups to snatch data in memory instead of mapping over arrays. But it was too late.

Please don’t do this unless you really know all of the areas that are going to be affected.

What are some of the consequences?

If something changes from an array to an object, multiple areas of the app are at risk of becoming unfunctional. I made the biggest mistake to think that I had every part of the app that would be affected by a structured data change planned out in my mind, but there will always be that one spot left behind that was missed.


6. Developing Without Using Snippets

I used to be an Atom fan, but I switched to VScode as it provided features that I find very valuable in the development flow like Project Snippets.

If you’re using VSCode, I highly recommend you download it if you haven’t already. This extension lets you declare custom snippets for each workspace for that project. It works exactly like the built-in user snippets feature that comes in VScode by default, except you create a .vscode/snippets/ folder inside your project like so:


7. Ignoring Unit/E2E/Integration Tests

As the app grows larger, it becomes scarier to edit existing code without any sort of tests in place. You might end up editing a file located at src/x/y/z/ and decide to push the changes to production, however if the change affects another part of the app you didn’t notice, the bug will stay there until a real user catches it while they are browsing since you won’t have any tests to alert you beforehand.


8. Skipping the Brainstorming Phase

Developers often skip the brainstorming phase because they aren’t coding, especially when they’re given a week to develop a feature. However, from experience, this is the most important step and will save you and your team a lot of time in the future.

Why bother brainstorming?

The more complex an application, the more developers have to manage certain parts of the app. Brainstorming helps eliminate the number of times you refactor code because you already planned out what could go wrong. Often times developers are hardly given the time to sit back and apply all the neat practices to enhancing the app further.

This is why brainstorming is important. You think of all the code design in architecture and the enhancements you’d need so you can tackle them all from the start with a strategical approach. Don’t fall into the habit of being overly confident and planning it all in your head. If you do, you won’t be able to remember everything. Once you do something wrong, more things will go wrong like a domino effect.

Brainstorming will make it a little easier for your team as well. If one of them ever gets stuck on a task, they can refer to the brainstorming they had from the beginning and it’s possibly already there.

The notes you take in brainstorming ideas can also serve you and your team as an agenda and help in easily providing a consistent sense of your current progress when developing the application.


9. Not Determining the UI Components Beforehand

If you’re going to start building out your app, you should decide on how you want your app to look and feel. Several tools are available to help you with creating your own mockups.

One mockup tool I hear about often is Moqups. It’s fast, does not require any plugins and is built in HTML5 and JavaScript.

Doing this step is very helpful in giving you both the information and data that is going to be on the pages you create. Developing your app will be much more of a breeze.


10. Not Planning the Data Flow

Almost every component of your application will be associated with some kind of data. Some will use their own source of data, but most of them will be provided from a location higher up in the tree. For parts of your application where data is shared with more than one component, it’s a good idea to make that data available higher up in the tree where it will act as a centralized state tree. This is where the power of Redux comes to the rescue :)

I advise making a list of how the data is going to flow throughout your application. This will help you create a firm mental and written model of your app. Based on these values, your reducer should easily be established from it.


11. Not Utilizing Accessor Functions

As the app gets bigger, so do the number of components. And when the number of components increase, so does the number of times you use selectors (react-redux ^v7.1) or mapStateToProps. If you find your components or hooks often selecting state slices like useSelector((state) => state.app.user.profile.demographics.languages.main) in several parts of your application, it’s time to start thinking about creating accessor functions in a shared location where the components/hooks can import and use from. These accessor functions can be filterers, parsers, or any other data transformation functions.

Here are some examples:

src/accessors

connect version

src/components/ViewUserLanguages

useSelector version

src/components/ViewUserLanguages

It’s also very important to keep these functions immutable, free of side effects, as covered here.

As an alternative, another popular solution is reselect specifically created and optimized for redux as explained here. If you use it just like any of the accessor functions mentioned above, you’ll have the added benefit of computed values being cached — where these functions will only recompute when one of its arguments changes. The more expensive computations you have in your components, the more this solution becomes highly recommended. (thanks healqq).

12. Not Controlling the Flow in Props With Destructuring and Spread Attributes

What are the benefits of using props.something versus something?

Without destructuring:

const Display = (props) => <div>{props.something}</div>const Display = ({ something }) => <div>{something}</div>

With destructuring, you’re not only making your code more readable for yourself and others but you’re also making straight forward decisions on what goes in and what goes out. When other developers edit your code in the future, they don’t have to scan through each line of code in your render method to find all of the props that the component is using.

You also benefit from the ability to declare a default prop right from the beginning without having to add any more lines of code:

const Display = ({ something = 'apple' }) => <div>{something}</div>

You might have seen something like this before:

This is not only a little bit harder to read, but there is also an unintentional bug occurring in this component. If App also renders children, you have props.children being rendered twice. This causes duplicates. When working with a team of developers other than yourself there are chances that these mistakes can happen by accident especially if they aren't careful enough.

By destructuring props instead, the component can get straight to the point and decrease chances of unwanted bugs:


Read: 10 Things NOT To Do When Building React Apps

Conclusion

That is all, folks! I hope these tips helped you. Feel free to shoot me a comment/message with any questions and/or concerns! See you next time!

Better Programming

Advice for programmers.

jsmanifest

Written by

Team Lead Front End Developer for TeleMedicine. Follow and Join Me on my Adventures. https://jsmanifest.com

Better Programming

Advice for programmers.

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