So you’re probably aware that you might not need Redux, but maybe Redux is a good fit for your app. That means we should use it for managing all of our application state… right? Wrong.

If you followed along with my Building a Web App From the Ground Up series, you probably already are using Redux in your project.

But just because we’re using Redux doesn’t mean the state from all of our components needs to go through Redux. Time and time again I see people starting to use redux and creating new action types, actions, and reducers, for every important data type in their app. But you don’t want to put something in Redux because it’s important. You should put something in Redux because it would otherwise have a very complex flow of data.

If you’re not familiar with the general architecture of how Redux works, consider giving this a read first.

One API-Driven Component

One of the most common times I see someone using Redux extraneously is when they’re adding a new component that requires a new API call to be made. They’ll create a new thunk (async redux action), several other actions to handle success vs. failure cases, a new reducer to capture all of that data, and then they’ll only connect a single value and action to one component.

An API-Driven Component's Flow of Data through Redux
An API-Driven Component’s Flow of Data through Redux

That seems like a lot of overhead just to get results from an API.

My preferred approach to this kind of situation is really simple: Make the API request in your componentDidMount or a useEffect hook, put your result and loading/error indicators in your component state, and call it a day. It’s simple, it’s fast, and it’s easy to test with your favorite React testing framework. Most importantly, you can always migrate to Redux super easily later on if it turns out this data is required by multiple components.

An API-Driven Component's Flow of Data Not Using Redux
An API-Driven Component’s Flow of Data Not Using Redux

Multiple Presentational Components On The Same Page

The single component case is a pretty easy situation to spot over-engineering in. But what about when we have multiple components? Well, as long as those components are all on the same page, I really don’t see a big need for Redux still. As long as those components are all presentational. Or as long as the data is only being mutated in one place.

To facilitate this data flow, I would put your API request logic in one of the parent components of the 2 or more components where the data will be visible. How do I decide which component to put the logic in? Well, a common pattern in this type of application is to have Container components that are doing things like hooking up to your stores, making API requests, etc. If this is how your application is structured, I’d recommend putting it there to fit well with the current design.

Although, I generally feel like developers can easily bloat containers. I think they’re prone to code-smells, but I can talk about why containers stink in a future post. If you don’t have that kind of design in your code, I would just recommend putting it in component most immediately connect to both of your child components.

A component hierarchy not using redux
A component hierarchy using component state

Multiple Presentational Components On Different Pages

Now, what if you have some components that are visible in different routes? What do you do when those components need to fetch data from the same API? In this case, I actually recommend leaning into the Redux framework.

Using Component State for Components on Different Routes

While it’s not a complicated flow of data, the above example starts to get much more complicated when your lowest common ancestor component is separated from your child components by a router layer.

If you still attempted to do the local-state method here, which is possible, it would likely look something like this:

Multiple API-driven presentational components on different pages using component state
Multiple API-driven presentational components on different pages using component state

Now, this doesn’t look too complicated. But there are a few caveats to this diagram. Typically, if you were to try to implement this in React using something like react-router, you probably wouldn’t be passing props like your API results through the physical Router component. You would pass them in as a prop to the specific Route. This adds two extra connections to our diagram between the Lowest Common Ancestor and the two Route components.

Controlling renders with your Route components can also be complex, since most of the time you’re not implementing the shouldComponentUpdate method on your Routes, they’re likely just going to re-render when you receive new props. This is an issue because there are likely a lot of other components in that tree that don’t need to re-render based on those API results you’re trying to pass through.

Additionally, passing props through your router to the top-level component for a route can itself be a bit of a challenge. Now, nicely designed routers like react-router can make this incredibly easy like so:

<Switch>
  <Route path="/list">
    <MyDataList myData={result} />
  </Route>
  <Route path="/add">
    <MyDataForm myData={result} />
  </Route>
</Switch>

But if you’re using a sub-par routing solution, it can get a bit hairy.

Using Redux for Components on Different Routes

If you’re using Redux, you can write a simple action that fetches the data if it hasn’t been fetched yet. Then, you can connect your two child components directly to the store.

API-Driven Components Using Redux
API-Driven Components Using Redux

Notice that in the above diagram, it doesn’t even matter that the components might be on separate routes. This design is super flexible. It allows you to add any number of additional components to this with little overhead. You can move the child components around in your component tree with very little effort. You can add additional API methods to be used in your components with very little effort. It’s why redux is so great for more complex data flow.

Multiple Data-Mutating Components

Another situation where I put new data into my redux flow is when I have multiple components that are modifying data. Now, if the modifications are super simple, this can still be done using an architecture with a lowest common parent managing state and child components underneath that, like we did in the Multiple Presentational Components On The Same Page section.

When you have multiple things mutating data, the flow that Redux provides will help you keep things simple. As your data interactions get more complex, the logic in your parent component will be more prone to bugs. That’s why when I have multiple components mutating the same data, I put it in Redux.

When should I consider adding Redux to my project?

All of these tips are working under the assumption that your project already has Redux added to it. It’s my philosophy that just because your project has Redux, it doesn’t mean you have to put everything in redux. Redux should be reserved for more complex data flows that would lead to some buggy code. If you’re trying to figure out whether or not you should add Redux to your project, I really recommend Dan Abramov’s post on why You Might Not Need Redux.

Have some more ideas on when you should/shouldn’t put state in Redux? Comment them below!

Recommended Posts

No comment yet, add your voice below!


Add a Comment

Your email address will not be published. Required fields are marked *