While the answer matt clemens posted does cover this at a high level, but I'll try to go into more depth here.
You can use connect()
at any level. Doing so makes the component smart, since it knows where its props
come from. A dumb component just has props
, and they could come from anywhere. A smart component is coupled to redux; a dumb component is not.
There are differing opinions on this approach, but it is supported and valid.
Where to draw this line is entirely up to you, but let's look at an example. You have some chat client with a standard sidebar component, a message window, and the entry field for sending new messages.
+---------+--------------------------+
| | |
|Sidebar | Messages window |
| | |
| | |
| | |
| | |
| +--------------------------+
| | New Message Entry **|
| | |
+---------+--------------------------+
The parent of all of these would use connect()
to get the data from redux and feed it into these components via props. Now imagine that those two asterisks besides new message entry
open up a settings panel (ignore the stupid placement, this is an example). Does it really make sense for new message entry
to pass those props through? No, it doesn't.
To solve this you could create a special "container", lets call it SettingsContainer
that used connect()
to get its props and all it did was pass them down to SettingsPopup
. SettingsPopup
would not know about redux, and could still be tested/styled/reused normally, and the new message entry would only need to know about SettingsContainer
, not any of its dependencies.
This approach scales well, but it has two penalties. First, the smart "wrapper" components like SettingsContainer
have to be consumed by otherwise dumb components. This complicates the testing of the new message entry component. Second, the top-level component no longer exposes the entire graph of data dependencies, which makes things harder to reason about without delving deep into the component hierarchy.
These tradeoffs can be worth it, but you should be aware of them.