Why you should stop using the “container/presentational” pattern in Redux
By: Matan Borenkraout
Tags: Frontend, React, Redux

TL;DR:

The redux style guide has an important rule: “Connect More Components to Read Data from the Store”. This rule has some important performance aspects that are covered with an example in this post.

Photo by frank mckenna on Unsplash

The “Container/Presentational” Pattern used to be the best practice when using redux or any other store management package.

It was explained thoroughly by Dan Abramov and Michael Chan.

If we want to keep it short, a container usually holds the data fetching logic and data access to store. On the other hand, a presentational component is more generic and just present the data it gets as props .

This pattern was common before React Hooks were introduced and probably every developer using React with classes has seen or used it.

But in the world of hooks, this approach can cause serious performance issues (we will cover those later on).

Moreover, the Redux best practices now suggests we should connect more components to read directly from the store (which is quite the opposite to this pattern).

“Prefer having more UI components subscribed to the Redux store and reading data at a more granular level. This typically leads to better UI performance, as fewer components will need to render when a given piece of state changes.” (The redux docs)

But, what’s the reason behind this rule?

Our example app

I’ve built a small app with a sidebar fetching all the Pokemons (with a Select that decides how many Pokemons to fetch), and a right box showing the selected Pokemon. Clicking on a Pokemon name at the left, will change the image and fetch the relevant data.

The UI looks like this:

In the first approach, I decided to use connect on a container , query all the data from the store and also mapDispatchToProps so I’ll be able to dispatch some data fetching.

I passed the data to my components using props (the example uses styled-components but the actual styles were left behind):

My components just fetch the data and show it based on the props.

I opened the React profiler, recorded a click on a Pokemon name and saw the renders that it triggered.

Here’s the screenshot:

What do we see here?

When clicking on a Pokemon name on the left sidebar, we initiated two renders. In the first one we see the PokemonsSidebar was rendered and it took 1ms. But, why was it even rendered? It didn’t change, nor did it’s props. The reason for that is that the parent component was rendered because of a change in the redux store, so all of it’s children should also render.

We can also see that the whole Render duration was 13.6ms, that’s because inside the PokemonsSidebar we have 150 li ‘s that rendered again for no reason because their parent re-rendered.

In the second approach, I refactored my code to useSelector and useDispatch . My presentational components will be connected directly to the store and dispatch the actions.

Here’s my component’s code:

Let’s see the profiler screenshot:

As we can see here, the PokemonsSidebar didn’t re-render because it wasn’t subscribed to the selectedPokemon data from the store, thus all of it’s children didn’t re-render. The Render duration this time is only 0.9ms (compared to 13.6ms)!

These numbers may sound negligible but that’s just an example. My app isn’t complicated and there’s just one component that shouldn’t render (and it’s children). What if I had more components? and my components had a heavy render?

Why does it happen?

Since we’re using function components, whenever our parent component renders, the whole tree is marked for render. React then recurses over all children and tries to render them. If our function components aren’t wrapped with React.memo they will automatically re-render even if their props or state didn’t change. We need to bear in mind that memoization also has some performance impact so wrapping all of our components with React.memo isn’t always the best approach.

Disclaimer

As you can understand from the last section, this isn’t a Redux only issue, but rather a React behavior. The “container/presentational” pattern can still be a valid solution as long as the impact is taken into consideration.

It’s important to note that this isn’t just a useSelector vs connect issue. I’ve seen examples creating a container with lots of useSelector s just aiming to mimic this “container” approach and I’ve also seen examples of connect on many components.

Summary

In this post, we’ve covered an important rule that was somewhat hidden inside the whole Redux style guide. If you still haven’t read the style guide, I urge you to do it. It contains many rules that will make your work with Redux straightforward and clear.

I hope everyone’s feeling well and keeping themselves safe!

If you have any questions, I’m here and also on Twitter. Feel free to ask or comment, I’d love to hear your feedback!

Thanks,

Matan.

Read More:

The repo containing this example

The Redux best practices

Presentational and Container Components by Dan Abramov

Container Components by Michael Chan