Decoupling Layout from Data Flow
I think the thing I like most about Redux/Relay/Flux is the separation of concerns the containers provide. Without these containers, data has to flow down from parent to children. The highest parent has to concern itself with all the data of all of its children. With containers, each child just needs basic data (i.e. an entity id) in order to know which data to show. I like to say that this is essentially decoupling layout from data.
A Bias Towards IDs
I've been really happy. We chose early on, to adopt a bias to passing only entity IDs whenever possible, instead of passing data. Oh, by the way, using normalizr, so our data get's grouped by entity type.
For instance, when we want to display a list of some entity (say for instance a list of pickup trucks), a container would pass the list of IDs to a "list" component. The "list" component would iterate over the IDs and pass each ID to a "list item" component. The "list item" component's container would perform a lookup of the truck entity, and provide the truck data to the "list item" component.
This has made me very happy. I think it's much more efficient for React/Redux. I feel like our components are properly decoupled. I feel a certain amount of relief, knowing we have well-defined boundaries between components, and between data and layout.
Occam's Razor For Less Cognitive Load
It's true that the simplest solution is often the best. True, on a macro scale it may seem to be oversimplified, and at times inaccurate to say so. However, when taking into account the bigger picture, like developer cognitive load, fatigue, refactoring, and training new developers; Simpler, more consistent code is better for overall productivity.
I've found that providing generally applicable patterns to our team has led to a simpler onboarding process. For instance, early in our React/Redux based project, I created several code generators for creating components, action/reducers, and thunks. Our developers have been using these code generators for boilerplate code. It looks like this:
src/components/MyComponent src/components/MyComponent/MyComponent.js src/components/MyComponent/MyComponent.test.js src/components/MyComponent/container.js src/components/MyComponent/index.js
My generators create these files for a new component and add the imports and basic starter code. A few months later, looking at our repository, the component code is consistent. This makes it really easy to work on each other's components.
Every Component Is Born With a Container
Consistency is simplicity. Along those lines, every component is born with a container. This applies that bias towards consistency and tends to result in components that decouple data from page-layout. A case needs to be made by a developer who wants to make a component without a container. Most components need to display data or interact with application state.
In my experience, components that don't need to interact with application state are pure UX components. At Zillow, we have a unified design experience, which is shared across teams. Those pure UX components belong in a shareable repository outside of my application, which can be used across Zillow platforms.