Skip to content

Commit 75c679c

Browse files
committed
Merge pull request #2 from artsy/document_async_example
document the example
2 parents 9cf1bad + 53f6406 commit 75c679c

File tree

3 files changed

+63
-3
lines changed

3 files changed

+63
-3
lines changed

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ import AppLayout from '../components/app_layout';
3131
import * as mySelectors from '../selectors/my_selectors';
3232
3333
const controllerGenerators = {
34+
*initialize() {
35+
// ... any code that should run before initial render (like loading actions)
36+
},
3437
*onUserActivity(meaningfulParam) {
3538
const { dispatch, otherData } = yield getProps;
3639
dispatch(actions.fetchingData());
3740
try {
3841
const apiData = yield httpRequest(`http://myapi.com/${otherData}`);
39-
return dispatch(actions.fetchingSuccessful(apiData));
42+
retudrn dispatch(actions.fetchingSuccessful(apiData));
4043
} catch (err) {
4144
return dispatch(actions.errorFetching(err));
4245
}
@@ -52,4 +55,6 @@ export default controller(AppLayout, controllerMethodFactories, selectorBundles)
5255
5356
```
5457

55-
Better examples to come.
58+
## Example
59+
60+
To see an in-depth example of usage of this library, [the async example from the redux guide](http://redux.js.org/docs/advanced/ExampleRedditAPI.html) is ported to use the controller approach [in this repo](https://github.com/artsy/react-redux-controller/blob/master/example/README.md).

example/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Async example
2+
3+
This directory contains a port of [the async example from the redux guide](http://redux.js.org/docs/advanced/ExampleRedditAPI.html). Here, we point out some of the points of interest in using react-redux-controller.
4+
5+
## Structual differences from original example
6+
7+
Below are some of the fundamental ways in which the approach here differs from the async example:
8+
9+
### Dumb components
10+
11+
In line with Dan Abramov's [post on Smart and Dumb components](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.ho45ybvub), it's interesting to note that the controller approach hides the smart components entirely. Here, [all of the components](https://github.com/artsy/react-redux-controller/tree/master/example/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.
12+
13+
### Minimal use of manual `props` passing
14+
15+
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](https://github.com/artsy/react-redux-controller/blob/master/example/components/Layout.js#L31).
16+
17+
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.
18+
19+
### Context types to declare dependencies
20+
21+
The `contextTypes` annotation on components is used to formalize the dependencies between the controller and the components. See the [Layout component](https://github.com/artsy/react-redux-controller/blob/master/example/components/Layout.js#L38-L43), for instance.
22+
23+
### Use of selectors as the materialize view of the store
24+
25+
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](https://github.com/artsy/react-redux-controller/blob/master/example/selectors/index.js), components can be written to depend only on the derived data. Note also how the selectors are annotated with the prop types they return.
26+
27+
In real use, you might use tools like reselect and normalizr, as suggested in the Redux guide.
28+
29+
### Mapping of DOM event to domain event
30+
31+
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](https://github.com/artsy/react-redux-controller/blob/master/example/components/Layout.js#L18)'s handling of the refresh button.
32+
33+
### Dumb action creators
34+
35+
Compared to [redux-thunk](https://github.com/gaearon/redux-thunk) and similar approaches to handling asynchrony, [action creators](https://github.com/artsy/react-redux-controller/blob/master/example/actions/index.js) no longer contain any real logic, which now exists in the explicit controller layer.
36+
37+
## Practical usage
38+
39+
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.
40+
41+
### `*initialize` method for startup actions
42+
43+
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](https://medium.com/@mjackson/universal-javascript-4761051b7ae9), 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](https://github.com/artsy/react-redux-controller/blob/master/example/controllers/App.js#L10-14).
44+
45+
### `yield` to resolve promises
46+
47+
Use of [co](https://github.com/tj/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](https://github.com/artsy/react-redux-controller/blob/master/example/controllers/App.js#L46). The upside of this is that regular control flow can be used, interleaved with asynchronicity.
48+
49+
### `yield` to get state and dispatch
50+
51+
[Special symbols](https://github.com/artsy/react-redux-controller/blob/master/example/controllers/App.js#L4) 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`](https://github.com/artsy/react-redux-controller/blob/master/example/controllers/App.js#L17) to request these dependencies.
52+
53+
### `yield` to defer to subcomponents
54+
55+
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](https://github.com/artsy/react-redux-controller/blob/master/example/controllers/App.js#L22).

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-redux-controller",
3-
"version": "0.1.1",
3+
"version": "0.1.2",
44
"description": "Library for creating a controller layer to link React and Redux.",
55
"license": "MIT",
66
"keywords": [

0 commit comments

Comments
 (0)