Last time, I talked about one of the misconceptions about using React and Redux together: that only top-level components should be smart.

Today, I want to show another misuse of Redux I’ve seen in a real project, and how to avoid it.

The Case

Code speaks volumes and leaves less room for confusion. Here is a simplified snippet of the code we’re about to tear apart. Ready?

function mapStateToProps ({ tournament , match }) return { tournament , match }; } @ connect ( mapStateToProps ) class MatchInfo extends Component { static propTypes = { tournament : PropTypes . shape ({ active : PropTypes . shape ({ type : TournamentTypeShape , }), }), match : PropTypes . shape ({ active : PropTypes . shape ({ scores : MatchScoresShape , teams : MatchTeamsShape , }), }), }; render () { const { match , tournament } = this . props ; const { scores , teams } = match . active ; const { type } = tournament . active ; // use the extracted props } }

This is not the worst example of the anti-pattern, but it clearly illustrates the issue at hand.

The Bad

Now… what’s wrong, exactly, with the snippet above?

If you don’t, take a minute and imagine dozens more of components like this, then suppose you want to change the structure of the state.

The problem demonstrated in the snippet is, the visual is now tightly coupled to how the state is structured. It can be evidenced by the prop types alone — there is quite a bit of nesting there, with only a few actually useful properties.

mapStateToProps is dumb — it takes the state in and returns whole sub-trees of that state. We could just as well pass in the entire state to the component.

What it means is:

The component isn’t as reusable — you can’t tell it to render this score and those teams, you have to construct the entire sub-trees. Testing the component in isolation (both in code and Storybooks) would require props of that same state shape. Every time the state structure changes, all of the connected components would have to be updated.

Ouch!

The Better

“Okay, okay,” you say. “I see. Now how do I make it better?”

To answer that, let’s take a tiny step back and identify the core parts of how the snippet above operates:

there’s state, which represents our business and visual requirements. The state knows about its structure, but it doesn’t know how specifically it’s going to be used. then there’s mapStateToProps, which in our case is as good as a no-op. It just selects the sub-trees, but it doesn’t know why those are needed. finally, we have MatchInfo. It does know about the state structure, and also know what it needs from it.

Looked at this way, mapStateToProps notoriously stands out for not being any useful. MatchInfo also stands out for knowing too much.

Far from ideal. Back to The Better Way™.

What if we found a way to make use of mapStateToProps? What if we used it to translate the store structure into what MatchInfo needs — scores, teams, and tournament type?

The picture, then, would be:

state mapStateToProps bridges the state and MatchInfo MatchInfo accepts scores, teams, tournament type; doesn’t know how the state is structured.

Implemented in code, it can look roughly like this:

function mapStateToProps ({ tournament , match }) const { scores , teams } = match . active ; const { type } = tournament . active ; return { scores , teams , type }; } @ connect ( mapStateToProps ) class MatchInfo extends Component { static propTypes = { type : TournamentTypeShape , scores : MatchScoresShape , teams : MatchTeamsShape , }; render () { const { scores , teams , type } = this . props ; // use the extracted props } }

Definitely better now! Redux details are not leaked into the component anymore. Hooray!