This directory contains a port of the async example from the redux guide. Here, we point out some of the points of interest in using react-redux-controller.
Below are some of the fundamental ways in which the approach here differs from the async example:
In line with Dan Abramov's post on Smart and Dumb components, it's interesting to note that the controller approach hides the smart components entirely. Here, all of the components are stateless functional components, which lets them focus purely on UI structure and I/O. Even up to the root UI component, they are directly coupled only to React itself and other components.
In this paradigm, typical React props
passing is not used for general dependencies. Instead, it's used for intentional coupling between components. This might be an iterator-iteratee relationship, or configuration of a particular use of a general-purpose subcomponent. When the relationship is simply structural, no props
are passed. See the Layout component.
This small example doesn't really sell the benefits of this decoupling. But in a design with deeply nested components and a lot of user interactions, the branches of the component tree would be vastly simplified.
The contextTypes
annotation on components is used to formalize the dependencies between the controller and the components. See the Layout component, for instance.
As mentioned in the Redux guide, it's best to keep the store itself normalized. But normalization isn't the ideal way for the controller methods and the views to consume model data, especially when it comes to derived data. By storing all calculations in a selector layer, components can be written to depend only on the derived data. Note also how the selectors are annotated with the prop types they return.
In real use, you might use tools like reselect and normalizr, as suggested in the Redux guide.
In this example, the DOM belongs to the view layer, and the view performs any DOM manipulation before invoking controller methods. See the Layout component's handling of the refresh button.
Compared to redux-thunk and similar approaches to handling asynchrony, action creators no longer contain any real logic, which now exists in the explicit controller layer.
This library offers a number of ways of accomplishing basic controller tasks that differ from other approaches. This example helps to document exactly how it works in practice.
This magic generator method can be used to kick off any activity that might need to happen upon launching the application. In a universal app, the initial state would be delivered with the webpage, so that the client could boot right up into it's steady-state. But in a client-side application that needs to be able to make a cold start, like this one, this is the way to initialize. See the App controller.
Use of co allows ES6 yield
to be used to suspend controller generators on Promises, which are resumed returning the resolved value of the promise (or throwing exceptions, in the case of rejection). This is used in the example to fetch from the Reddit API. The upside of this is that regular control flow can be used, interleaved with asynchronicity.
Special symbols can be used to access the controller's props, as injected by react-redux. These include the raw state from the store and dispatch
for triggering state changes. Controller generators use yield
to request these dependencies.
Controller methods are composable. Behind the scenes, they are converted into regular method functions that return promises, and they are bound into a shared this
context. This means you can call other controller methods and use yield
await them, as seen in the App controller.