How to structure react + redux app?

Hello campers,

Few questions here now that I have built couple of apps with react and redux.

  1. Is it common to have separate containers folder with files that simply render a single child component just to receive state from redux and pass them down as props? Why do we need this mid layer instead of passing state directly to the component that will utilize it?
    This is the example app from official redux doc that does it. https://codesandbox.io/s/github/reduxjs/redux/tree/master/examples/shopping-cart

  2. Isn’t a container file a component as well? Wouldn’t it be better for a js file to go inside of components folder and call it something like shippingCart.js instead of shippingCartContainer.js inside containers folder?

  3. Checking props types. Is it necessary to check for props at every level? Looks like it checks for prop types when passed from redux and its child component checks again when parent container passes down the same props again.

  4. Role of reducer? I know it simply returns the state of the store but if I want filtered version of the state, is that logic handled in reducer or our react components that accesses the state?

I hope my questions are clear… Thanks in advance!

Hey @shimphillip,

First of all, consider container a component that receives an array as a state (part of the state) and has to render bunch of children. I guess it has to be clear why passing state to each child in this case is a no-no. In your case, ShoppingCart is a container for Product components…

If component has only one child I wouldn’t name it a container and as you pointed out it makes more sense to connect child to the store instead.

Prop types are not part of redux and actually, not even part of React. And if you decide to go with them, then yes, you shall check prop types on every component (level).

Reducer is a core of redux, you can use multiple reducers each handling part of a state and combineReducers(reducers) them… Reducer always returns state (or it’s part of the state) in whole, mapStateToProps is where you filter and decide what part of the state to use.

Hopefully this will make things clear :slight_smile:

1 Like

thanks @iikkaj that was really helpful to resolve my confusion!

The reason for separating containers from “dumb” components is that the dumb components are completely independent of implementation – they just take the props they are handed and wire them up to the displayed component. They only know “execute this thing when clicked”, or “show this array of objects”, but not what “this thing” does, or how that array is supposed to be fetched – those are up to the container component to manage. This makes it so you can switch out container implementations without having to touch the dumb components they contain.

In other words, dumb components are the reusable parts that present the UI for the (less reusable) smart containers.

1 Like

1 more question.

Is it better use default props from prop-types?

MyComponent.defaultProps = {
  items: 0
}

or assign default values when receiving parameters?

const MyComponent = ({items = 0}) => {

}

or both?

Components that just take some props and render them (ie generally no state whatsoever) are extremely easy to test and debug. Then you can have container components which actually connect to Reduce. In theory, this sounds good. Occasionally it is the natural way to to structure an application, and then it is very good because it makes things pretty nice to work witg. In practice, there are an awful lot of grey areas and if you dogmatically stick to this pattern imo you tend to end up writing quite a bit of redundant code and splitting stuff up when it doesn’t need to be split up, which just makes things hard to read and edit (YMMV). It is very common and it works ok.

Whatever makes sense to you. Personally, I am with you on this, other people have different ideas (and it completely depends on personal experience so everyone is right and everyone is wrong and like to argue about it 🤷)

Your choice. Do note this library from the Redux people is built to deal with what you’re talking about (computing derived data). It was realised that this is a common need by the author/s of Redux pretty early on. It’s a separate library rather than being built into Redux (its major use case is improving performance). It’s very simple and has a section in the Redux docs

When is it a good idea to follow this pattern? When a container has lots of components nested inside of it?

Otherwise, really appreciate your answers! I will definitely take a look at reselect library you introduced me. :clap:

Umm, this isn’t going to be a great answer, but “when it feels like it makes sense”. As an example of the difficulty of knowing where to seperate things out:

I have a state in an app I’m building that kinda looks like this:

state = {
  newItems: {
    itemIds: [1, 2, 3, 4, 5]
    items: {
      1: {...item1Stuff},
      2: {...item2Stuff},
      3: {...item3Stuff},
      4: {...item4Stuff},
      5: {...item5Stuff},
    }
  },
  currentItem: {...theCurrentItemStuff}
}

And I have a tree that looks something like (sorry, pseudocode with awful names):

<NewItems>
  <NewItemsList>
    <NewItemsListItem>
    <NewItemsListItem>
    <NewItemsListItem>
    <NewItemsListItem>
    ...etc
  </NewItemsList>
</NewItems>
  • <NewItems> talks to the Redux store. It gets state.newItems.itemIds from there, and gives it as a prop to <NewItemsList>.
  • <NewItemsList> is completely dumb: all it needs is a list of IDs, and it maps over them and renders a bunch of <NewItemsListItem> components.
  • <NewItemsListItem> needs to talk to the Redux store. It has been given an ID, but it needs to use that id to look up state.newItems.items[id], and that gets it the details it needs to render its children.
  • It’s children are mainly dumb and include stuff like a component that parses timestamps, or one that parses location, or a button that allows that particular item to be updated.
  • So on the latter child, the update button, I also want to use that button for the bit of the tree that renders out state.currentItem, and passing everything as props makes that much easier; the logic to allow the update is slightly different between the two, but I can make it work the same by passing the right props (a function to dispatch updates).

So I guess you’re kinda right, if I look at that, <NewItems> and <NewItemsListItem> could be classified as containers, whilst the others would be components. But <NewItems> has only a single child in that sketch (in fact it has three, but two are completely static, they’re a header and footer and they take no props at all, so I don’t think they matter much). And I haven’t made a clear distinction in my codebase, they’re all in the same folder, and it may well be important going forward to connect some components up or disconnect connected ones :man_shrugging:.

Also, I kinda get the feeling from reading stuff from Redux authors/maintainers that the recommended best practise is, more and more, to use connect more often, at a more granular level, which means more connected components, rather than trying to divide up into containers/components (which tends to be a bit arbitrary). Edit and on this last point, when react-redux can support hooks properly (which afaik won’t be for a little while because they need to revert the major change made to the architecture in the last version), instead of having to write the mapDispatch and mapState functions and then use connect, you’ll just be able to use{ReduxThing} inside a function component - as it becomes much easier to write, I think less stuff will be dumb, and the container/component distinction will get very blurry

2 Likes

When is it a good idea to follow this pattern? When a container has lots of components nested inside of it? Otherwise, really appreciate your answers! I will definitely take a look at reselect library you introduced me. :clap:

As soon as you work in a professional environment, especially if you work with a lot of TDD or testing in general, you will see that it makes your work much easier.

1 Like

Thank you for the lengthy and detailed descriptions!

I think I will go with more granular approach and hooking up components with connect more often than a universal parent container distributing state through props.

TDD and testing is something I am just beginning. Actually just started looking at jest and enzyme the other day.

Can you explain how using this approach makes testing easier? At the end you end up with the same state in components.

The aim is to unit test things, isolated from everything else, so you don’t really end up with the same state, each test just checks one specific thing

<MyComponent prop1={foo} prop2={bar} />

As long as that component only uses those props, I can give that component anything I want and check it renders, it is super easy. If foo is a string and bar is a number, I can give it any string and any number and check it works.

<MyConnectedComponent ...possiblySomeProps />

This is slightly harder, because I have to mock the Redux store so that it gets the data it expects. Instead of the test being isolated to just testing a component, I’m also testing something else (Redux & the global store)

It’s not that much harder for simple stuff, you do like

<Provider store={{foo: 1, bar: 2}} />
  <MyConnectedComoponent />
</Provider>

in the tests if you want it to connect via the store, but it can get complicated quickly

2 Likes