14

I'm working on a component that has many subcomponents, some of which are nested five deep. I'm interested in using redux for the advantage of having a single source of truth in a common state atom.

What I'm not understanding is the smart/dumb component recommendation and putting the provider above the main component and passing everything down via props. If I do this then I'd need to pass props down all the way to the fifth nested item so it can make a callback to dispatch an action or to look at some state that only it needs and not its parents. I understand this is for code reuse, but the subcomponents will never be used outside of the main component. What is the recommended solution here? Still use props?

Note: the author of this library requested we ask questions on StackOverflow. I'm mentioning this because SO seems to flag "best practice" questions as too vague.

acjay
  • 34,571
  • 6
  • 57
  • 100
Bradford
  • 4,143
  • 2
  • 34
  • 44
  • 1
    Possible duplicate of [How to handle global state data into deeply nested components in Redux?](http://stackoverflow.com/questions/34299460/how-to-handle-global-state-data-into-deeply-nested-components-in-redux) – enjoylife Dec 22 '15 at 22:58
  • 2
    This remains your choice - take a look at https://twitter.com/dan_abramov/status/668585589609005056 (got it back from my twitter feed, thought it was relevant at the time ;-) ) – topheman Dec 22 '15 at 23:17

3 Answers3

12

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.

Community
  • 1
  • 1
Kyeotic
  • 19,697
  • 10
  • 71
  • 128
  • 1
    _This complicates the testing of the new message entry component._ This can be solved by injecting the "smart" components (a.k.a. containers) into the "dumb" components. – Lars Nyström Jul 07 '16 at 09:43
  • @LarsNyström I am not sure how that would work. A demo would be much appreciated (probably also a valuable answer) – Kyeotic Jul 07 '16 at 15:21
  • I don't think there's a standard way of doing it, but I wrote about one idea on how it's possible in this answer: http://stackoverflow.com/questions/30311081/react-dependency-injection-or-similar/38010717#38010717 – Lars Nyström Jul 08 '16 at 09:30
5

You can use the newer React feature of context, via using 'react-redux''s component Provider. Using Provider will abstract away some of the implementation details of context, makes your markup quite expressive imho.

You basically setup a mini-global property, that all sub-components, dumb or smart can reference:

import React from 'react';
import {render} from 'react-dom';
import {createStore} from 'redux';
import {Provider} from 'react-redux'; //Special Redux component to help
import {reducers} from './reducers.js';


var DeepDumbChild = (props, context) => (
    <div>
        <pre>
            {JSON.stringify(data, null, 2)}
        </pre>
    </div>
)


class SmartChild extends React.Component {
    render() {
        /* Use the global-like context */
        let data = this.context.store.getState();
        return (
            <div>
                <DeepDumbChild data={data}/>
            </div>
        )
    }
}
SmartChild.contextTypes = {
    store: React.PropsTypes.object /* required */
}

/* No messy props :) */
var App = () => (<div>
    <SmartChild/>
</div>);


render(
    <Provider store={createStore(reducers)}>
        <App/>
    </Provider>,
    document.getElementById('app')
);
Ashley Coolman
  • 11,095
  • 5
  • 59
  • 81
  • context with seems to be an awesome idea for passing the redux store down to nested components . But why is there a big warning issued on the fb page against it ? **If you have to use context, use it sparingly.** _confused_ – semuzaboi Jun 22 '16 at 11:45
  • The way I think about it, is its the _one_ "global variable" you get to use in your application state. Like all globals, useful but dangerous. If you go by the name, use it for things that only change the "context", if I have a complex components ``, and ``, I might wrap them in ``, and ``. – Ashley Coolman Jun 22 '16 at 12:38
  • Is it possible that your SmartChild component can call action methods using dispatch? – Faisal Mq Oct 08 '17 at 07:46
3

UPDATE: If you want to try the approach below, have a look at https://github.com/artsy/react-redux-controller, which I recently published.

I think the best way to go is to map selectors to context at the root (container) component, rather than having a bunch of containers or using connect everywhere. I've done this in my own project and it's working beautifully. It adds a new pattern of annotating selectors with the PropTypes they produce. That let me use childContextTypes to allow all descendants to retain the same protections they have with propTypes. This is an advantage over Ashley Coolman's approach of passing down a big untyped object being passed down. I'm hoping to have time in the next couple days to factor it into its own library and publish.

In the meantime, I'd suggest making components smart, using connect liberally, rather than Tyrsius's approach of creating a whole bunch of containers, but that's just my opinion.

Community
  • 1
  • 1
acjay
  • 34,571
  • 6
  • 57
  • 100
  • Its [not my idea](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.olvmdi3fy), it comes from Dan Abramov (creator of redux) – Kyeotic Dec 23 '15 at 17:14