Redux: container vs component?

Created on 19 Sep 2015  Ā·  46Comments  Ā·  Source: reduxjs/redux

In the examples there are always a folder called "container" and one called "component". What is the thought behind this separation?

Are "containers" smart component or are they route components or something else? Should components under "components" always be dumb?

docs question

Most helpful comment

I call components encapsulated React components that are driven solely by props and don't talk to Redux. Same as ā€œdumb componentsā€. They should stay the same regardless of your router, data fetching library, etc.

I call containers React components that are aware of Redux, Router, etc. They are more coupled to the app. Same as ā€œsmart componentsā€.

All 46 comments

As for me, container is route's handler, which also pulls redux's state for that route. Then I pass down my state as prop.

For example,

containers/properties.jsx

import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect }  from 'react-redux';

import * as actions from 'actions/properties';
import Filter from 'components/properties/filter';
import List from 'components/properties/list';
import Pagination from 'components/properties/pagination';

class PropertiesContainer extends Component {
  render() {
    return (
      <section>
        <Filter filter={this.props.properties.params.filter} />
        <List items={this.props.properties.items} isFetching={this.props.properties.isFetching} />
        <Pagination pagination={this.props.properties.params.pagination} />
      </section>
    );
  }
}

function mapState(state) {
  const { properties } = state;

  return { properties };
}

function mapDispatch(dispatch) {
  return {
    actions: bindActionCreators(actions, dispatch),
  };
}

const Connector = connect(mapState, mapDispatch)(PropertiesContainer);

export default Connector;

and, for example,

components/properties/pagination.jsx

import React, { Component } from 'react';
import Pager from 'components/ui/pager';

class Pagination extends Component {
  render() {
    const { total, offset, limit } = this.props.pagination;
    const current = offset / limit;

    return (
      <Pager total={total} current={current} />
    )
  }
}

export default Pagination;

Reading from the links you provided:

"A container does data fetching and then renders its corresponding sub-component. "

I get more the feeling that "containers" are actually what in redux is called "smart components"... and routes/pages should get their own folder.

I'm not sure why "container" and "route handlers" are in any way related?

It's a very common practice to group your app's components by route. As @theaqua pointed out, it's also common to make each route handler/component a smart/container component. If you're using combineReducers, you'll often find yourself breaking up state, routes, and containers along the same lines. Hence, they often get associated together.

@ronag I didn't think you need make containers and pages. Why you can't put route handler & redux connector to state in 1 place? It's very handy. Keep it simple.

In my application I only have 3 routes/pages. On the other hand I have lots of independent panels/modules. Making one huge connect is not feasible I need to at least split it up in terms of panels.

If I do all the mapping from state to props and all data fetching in a single file it would be unmaintainable. Especially if I do all the data fetching at the same location.

If you have 3 routes, so you need 3 route handlers. For example, A.jsx, B.jsx and C.jsx in /containers.

In each container you pull part (not entire!) of redux state and actions binds. Then you pass down this to components like in my example. It's very maintainable, because I (and my team) know ā€” connect to redux and actions binds always in containers, it's never will be in components (which is small and often stateless as in my example).

I think I understand your suggestion. However, as I said those 3 files will become very complicated if I put all my data fetching there. In our cause it would basically be "Login, App, Logout" where App would contain almost everything.

Maybe I'm too biased to Relay where each component has a container which describes what data to fetch and how it should look.

I'll try your suggestion and see where we end up.

I call components encapsulated React components that are driven solely by props and don't talk to Redux. Same as ā€œdumb componentsā€. They should stay the same regardless of your router, data fetching library, etc.

I call containers React components that are aware of Redux, Router, etc. They are more coupled to the app. Same as ā€œsmart componentsā€.

@gaearon: Perfect. That clarifies the examples. Thank you.

@gaearon So "dumb components" are stateless components, which can be written in a simpler syntax Since React 0.14, for example:

var Aquarium = (props) => {
  var fish = getFish(props.species);
  return <Tank>{fish}</Tank>;
};

Am I correct?

@soulmachine yep.

Not necessarily. Syntax is not the point.

"Dumb" components aka "presentational" components are components that receive all data by props, aren't aware of Redux, and only specify the appearance but not the behaviour.

@theaqua @gaearon Thanks!

Although the term "container" is already quite popular in the react/redux nomenclature, we decided to call them "connectors" in the project I'm working on, to avoid any confusion with layout/graphical containers.

btw, how to call them was previously discussed here rackt/react-redux#170. I keep everything inside a components folder and presentational ones one level deeper, inside components/presentational, and I think it's fine because they're unlikely to be needed by parts of the app other than containers.

@gaearon so is a component that dispatch considered "dumb" or "smart"? It is particularly useful to have a leaf or intermediate component to dispatch an action instead of bubbling the event back to top component to do the dispatch.

Yeah it can be occasionally useful although I prefer to connect() such components so action creator is injected as a prop. It doesn't really matter how you call it :-)

What about when you're building a module of components? For example, suppose i have a separate node module for our navigation menu <NavMenu> I want people to do code like this:

import {NavMenu} from 'my-redux-aware-components';

export function myPage(props) {
  return (<div><NavMenu routes={props.routes} /></div>);
}

so do I just name it 'NavMenuContainer'? that seems weird to me. Should I name the component NavMenuComponent instead? both seem weird to me. In this case the component only calls connect to get 1 field from the state. Is it really so bad to just inline the call to connect like this?

export default const NavMenu = connect(state => ({currentPath:state.routing.path})(React.createClas({...}));

Curious to hear your thoughts @gaearon on when (if at all) it's "ok" to just inline the connect call...

It doesn't matter how you call it. Why not call it NavMenu?

I don't understand the question about inlining.
It would help if you presented two approaches you're comparing.

ok so for example.
option 1 (2 separate files, 1 for container, 1 for component)

containers/NavMenu

import {connect} from 'react-redux';
import NavMenu from '../components/NavMenu';

export default connect(state => ({currentPath:state.routing.path})(NavMenu);

option 2 (1 single file containing both):
components/NavMenu

import {connect} from 'react-redux';

export default connect(state => ({currentPath:state.routing.path})(React.createClass({
  render() {
      return <div>the menu {this.props.currentPath} goes here</div>;
  }
});

what I mean by inlining is just having a single file (as opposed to 2 files) which is both the container and the component since there's only the 1 little bit about the currentPath that's needed from state. Is this just something that should always be avoided or is it reasonable to do it in simple cases like this?

Sure, it's reasonable to do this in simple cases.

ok cool. when would you draw the line and say "ok I'd move this into 2 separate files"?

When the component starts to mixes data concerns (how to retrieve / calculate data, how to dispatch actions) with presentation (how it looks). When it becomes hard to test or reuse in different context.

@benmonro One more thought. If you're building a module of components, you would sometimes want to connect these components to different parts of your app's state tree, or test their presentation separately with various states. In this case, having connect inside this module of components would limit this ability.

thanks @gaearon

@sompylasar very good point! thanks

hmm ok after going through my code to refactor it to split it out, I now have a follow up question. suppose that I also have a <NavMenuItem> container. The <NavMenu> component needs to reference <NavMenuItem /> as a child element. Should i just do import NavMenuItem from '../containers/NavMenuItem' in the NavMenu component? How does this affect testability @sompylasar?

I would make the NavMenuItem a pure presentational component with all needed data passed into it via props by NavMenu. This would allow to test it separately. So, you would have two presentational (NavMenu, NavMenuItem) and one connected ( connect(...)(NavMenuItem) ) component.

A thing I'm missing in this discussion: When you have them in one file, you can't use shallow rendering for testing. I've seen people expose both the presentational component and the container component to work around this, and test then separately, so in this case I'd rather have two files to make it explicit that these are two things. This also highlights the separation of concerns here and the fact that the presentational component is independent and reusable.

FWIW I updated the Presentational and Container Components article to reflect my current thinking.

We no longer discourage creating container components in the updated docs.
http://redux.js.org/docs/basics/UsageWithReact.html

@gaearon in your actual example in the Redux doc seems that components can have containers as child.
Am I wrong? How can this affect testability of a dumb component that render inside it a smart one?
However I don't find the way to let components be the latest ones in the hierarchy...

I have an app that render a Simple Data List component (Dumb).
Inside it every item has to be connected to the store, so it's a Smart one.

It's ok according to the doc, but can it bring some problem?

Thank!

How can this affect testability of a dumb component that render inside it a smart one?

This does make testing a little harder to set up (you need to initialize the store as well). When this is an inconvenience, extract more presentational components that accept children so you can pass container components inside. In general, the division is up to you, and you need to evaluate the tradeoffs (ease of writing, refactoring, testing, etc), and choose how to separate components for yourself.

Ok, so there's no right way. We have to evaluate case by case.. Many
thanks!!

Il lunedƬ 8 febbraio 2016, Dan Abramov [email protected] ha
scritto:

How can this affect testability of a dumb component that render inside it
a smart one?

This does make testing a little harder to set up (you need to initialize
the store as well). When this is an inconvenience, extract more
presentational components that accept children so you can pass container
components inside. In general, the division is up to you, and you need to
evaluate the tradeoffs (ease of writing, refactoring, testing, etc), and
choose how to separate components for yourself.

ā€”
Reply to this email directly or view it on GitHub
https://github.com/rackt/redux/issues/756#issuecomment-181143304.

Luca Colonnello
+39 345 8948718
luca.[email protected]

What about 'layout components'? I mean when you have many containers that need to be in a single component to pass it to the router/navigator, this wrapping component is going to be a 'dumb presentational container of containers' right?

@Emilios1995 I have the same issue...
I have a Page component that use inside a layout component.
This Layout component have menus, headers, footer.. The content is the child passed by Page to Layout..
Menus and headers are container!! So Layout potentially is a container, but isn't connected to the store..

However, if I try to pass to layout the menus and the header, i have a page (container) that render layout (component) and passing to it menus and header (containers).

Doing this the hierarcy is correct, but I have to repeat menus and header in every page, and bith of them are the same in every page for me..

@LucaColonnello I donā€™t quite understand the issue without the code. May I ask you to create a StackOverflow question with a simple example illustrating your problem? Iā€™d be happy to take a look.

ASAP

Il sabato 27 febbraio 2016, Dan Abramov [email protected] ha
scritto:

@LucaColonnello https://github.com/LucaColonnello I donā€™t quite
understand the issue without the code. May I ask you to create a
StackOverflow question with a simple example illustrating your problem? Iā€™d
be happy to take a look.

ā€”
Reply to this email directly or view it on GitHub
https://github.com/reactjs/redux/issues/756#issuecomment-189672067.

Luca Colonnello
+39 345 8948718
luca.[email protected]

I think Dan's article on medium
https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.3y00gw1mq
clarifies all the issues...

2016-03-01 18:19 GMT+01:00 Emilio Srougo [email protected]:

@gaearon https://github.com/gaearon It's not an issue, I think it's
rather a question which I've made here:
http://stackoverflow.com/questions/35729025/should-the-route-handlers-use-containers-or-presentational-components?noredirect=1#comment59133192_35729025

ā€”
Reply to this email directly or view it on GitHub
https://github.com/reactjs/redux/issues/756#issuecomment-190820426.

Luca Colonnello
+39 345 8948718
luca.[email protected]

@gaearon I wonder that if a presentational component may contain container components inside, how can it be reusable?
FYI:

I wonder that if a presentational component may contain container components inside, how can it be reusable?

If all its usage scenarios include a specific container inside, I donā€™t see whatā€™s not reusable about it. And if not, make it accept this.props.children and create other presentational components that pass specific containers or presentational components inside.

@gaearon Is [Root component] container component on React-Router?

  • route.js
<Route path="/" component={Root}>
      <IndexRoute component={Main} />
      <Route path="/account/signIn" component={SignIn} />
</Route>
  • root.js
export default class Root extends React.Component {
  render() {
    return (
      <div>
        <div id="container" className="container">
          {this.props.children}
        </div>
      </div>
    );
  }

Thanks.

@gaearon above you said that you prefer to connect components in order to gain access to dispatch (as opposed to passing it down through from parent components).

If you connect these components, and they also have props which _could_ be mapped from the reducers which are currently being passed in by the parent, would you refactor these to come from connect? Or use ownProps to keep them passed by parent.

Is there a functional/performance difference between the two options?

I don't have much experience working with huge redux projects but I've been researching and thinking a lot about optimizing react/redux file structures. Let me know if this makes any sense or not:

src/
  components/
    header/ 
      navigation.js # nav menu list
      index.js # Header component
  modules/
    header/
      actions.js # header actions (sticky scroll, collapse mobile menu, etc...)
      reducer.js # header actions reducer (export to modules index)
      index.js # HeaderContainer (connect to Header component)
    index.js # combineReducers and export default configureStore (and middleware)

another concept:

src/
  components/
    navigation.js
    logo.js
    link.js
    list.js
    item.js
  modules/
    header/
      actions.js # header actions 
      wrapper.js # header class (smart) component - to wrap header with functionality (was previously classified as container)
      container.js # header functional (dumb) component - to contain universal components
      index.js # header actions reducer - to export into modules rootReducer 

Or is it just better to keep components, containers and redux modules separate (even though they share the same name)? Thanks for any input.

I have worked with enterprise level react-redux projects and through my experience, I can say one thing that it solely depends on you how to define the architecture of your project. I don't follow any of the container/component based architectural variations because they are impractical in the sense that React's purpose is to make reusable components based UI.

So I have come up with a simple approach of grouping entire projects based on Modules and it has worked really well till now in terms of scalability, maintainability, and code readability.

image
image
image

For me, container and smart-component are exactly the same. The simple definition is:
A container/smart-component is the one which contains JSX markup + event handlers + api calls + redux's connect/MSTP/MSDP.
A dumb component is PURELY presentational, functional component.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ramakay picture ramakay  Ā·  3Comments

amorphius picture amorphius  Ā·  3Comments

jimbolla picture jimbolla  Ā·  3Comments

olalonde picture olalonde  Ā·  3Comments

benoneal picture benoneal  Ā·  3Comments