I'm new to redux and react-redux, in the mean time I am trying to make a redux app.
I don't understand the statement on redux document:
Then, we wrap the components we want to connect to Redux with the connect() function from react-redux. Try to only do this for a top-level component, or route handlers. While technically you can connect() any component in your app to Redux store, avoid doing this too deeply, because it will make the data flow harder to trace.
Isn't it easier to connect to all components and when state is updated, every component can get the new state tree?
Why dumb components and high level containers?
Thanks.
As mentioned, Abramov (Redux author) has walked-back his advice re: connect()
ed components. He articulates a nice rule of thumb in this Reddit post on the topic:
I do it this way:
Start by using one container and several presentational components
As presentational component tree grows, “middle” components start to pass too many props down
At this point, I wrap some leaf components into containers so that “middle” components don’t need to accept and pass down props that are completely unrelated to them
- Repeat
If you watch the last ten videos from my course on Egghead, this is exactly the approach I demonstrate: https://egghead.io/series/getting-started-with-redux
From my reading, the initial advice on connect()
had nothing to do w/ performance and everything to do with modular components & ease-of-reasoning about data flow in larger apps.
In fact, more connect()
ed components might be a performance advantage vs. the 1-container-to-rule-them-all top-heavy pattern. Abramov once more:
Both approaches are fine, and having nested connect() components is actually going to give you more performance. The downside is they're slightly more coupled to the application and slightly harder to test, but that may not be a big issue.
When I had one container at the top, I had efficiency problems because React rerendered all my components during slightest update somewhere in the tree. So I abandoned that approach and made my app against docs, which turned out to be faster.
But later I've seen that even Redux author wrote on his Twitter:
Emphasizing “one container component at the top” in Redux examples was a mistake. Don’t take this as a maxim.
https://twitter.com/dan_abramov/status/668585589609005056
and
Try to keep your presentation components separate. Create container components by connecting them when it’s convenient.
https://twitter.com/dan_abramov/status/668586001175048192
The answer is in this section from your excerpt of the docs:
While technically you can connect() any component in your app to Redux
store, avoid doing this too deeply, because it will make the data flow
harder to trace.
One of the core principles of Redux is data should generally flow from the top down, i.e. it should be unidirectional. If you connect
too many lower level components, your data flow is no longer unidirectional. The main consequence of this is that it is much easier to have inconsistent state among your components.
When going top-down, which is what naturally happens when you only connect a limited number of high level components, it is much harder to create situations where you have inconsistent state, hence the advice in the docs.
In some cases, you can use connect()
ed components deeper down. I wouldn't interpret the documentation so strictly.
Generally speaking, if you find yourself passing down too many props from your components and the components doing the passing aren't using those props, then they can be moved to a separate container component.
If you find yourself constantly writing props down a component chain it might be time to add a container:
// A blog post view component
render () {
const {post} = this.props;
return (
<div>
<h1>{post.title}</h1>
<Author author={this.props.author}
onClick={this.props.favAuthor}
onHover={this.props.authorDetails}
isAuthorFaved={this.props.isAuthorFaved}
isAuthorFollowed={this.props.isAuthorFollowed}/>
</div>
)
}
In this case, the post component doesn't have any use or need of any props used for <Author/>
. You might want to consider making an <AuthorContainer author={this.props.author}/>
instead. The author belongs to the post, so you will need that bit of information. The rest can be computed using state in the container's mapStateToProps(state, ownProps)
function where ownProps.author
is the author object.
Again, this is a contrived example, but it is really up to you in the end where the logic belongs.