Eleni Chappen

What I’ve learned from a few large-scale React and Redux projects

Dec 18 2017

A friend of mine recently starting a new job where he’ll be working a lot in React and Redux, and asked me if I had any strategies or best practices, particularly with large-scale projects.

I wish I could tell him that I do. But honestly, every time I make a framework-specific rule, I quickly come across a situation where the best solution is to break it.

What I have come up with are a few benchmarks that guide me to making sure my React code is understandable to other developers, and to Future-Me.

1. All of my logic is tested

When I ensure that all of my logic is tested, whether it’s component/view logic or action-related logic, I naturally fall into healthier coding patterns. I extract unnecessary responsibilities from my components. I write more helper functions and utility modules. I can better determine when state should be kept locally or remotely.

This also helps me determine what packages I want to introduce to my app. For example, people often debate whether Redux is an appropriate tool for their project. The co-author of Redux has even said that for many cases, local state is just fine. In my view, if Redux helps you test your logic, then you are gaining a big advantage by using it. Since reducers are supposed to be pure functions, testing them is incredibly straightforward. Action creators can be tested in isolation outside of your components. If you use any middleware to perform asynchronous requests, these can be tested in isolation as well.

Testing view logic should not be ignored either. If testing your components becomes cumbersome, or if you’re stubbing too many functions in order to test them, that’s a good sign your component is doing too much.

2. My Redux actions tell a story

Redux Dev Tools is indispensable to me when I’m building an app, and when I’m debugging one. These tools allow you to view all of your actions in real time, rewind and replay actions, and dig into the current state for each action.

The list of action names that Redux Dev Tools gives you should tell the story of your user. They should give you a crystal clear understanding what what your user is doing at any given time. An action like SEND_API_REQUESTED is way less understandable to a new developer than an action like USER_LOG_IN_REQUESTED, because it lacks the context of the user experience. Giving your actions this context helps a lot when debugging.

Redux actions should also show you the result of any asynchronous requests you make. So if I see a USER_LOG_IN_REQUESTED in my list of actions, I should know to expect a USER_LOG_IN_SUCCEEDED or USER_LOG_IN_FAILED action shortly afterward.
Using a redux middleware like redux-saga helps me keep track of asynchronous requests and their effects. With dev tools I can clearly see the order of these requests and the effects of their responses.

3. I know how to use a React component without looking up a working example of it in the codebase

If I’m looking at a component definition, I should be able to:

  1. determine whether this component is for global use or context-specific use. Often this is achieved by defining the component within certain directories, like components/elements/forms/textInput.js.

  2. know exactly what props this component receives. This can be done by using React’s prop-types package, which now allows you to more accurately define the shape of your props. So instead of doing this:

user: PropTypes.object.isRequired  

I can do this:

user: PropTypes.shape({  
  id: PropTypes.number.isRequired,
  first_name: PropTypes.string,
  state: PropTypes.oneOf(['pending', 'active', 'archived'])

I feel like I’ve only scratched the surface here, but it’s a start. Happy coding!