Why you should stop using the “container / presentational” pattern in Redux
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.
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
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
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.
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
connect issue. I’ve seen examples creating a container with lots of
useSelectors just aiming to mimic this “container” approach and I’ve also seen examples of
connect on many components.
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 available on Twitter.
Feel free to ask or comment, I’d love to hear your feedback!