Skip to content

Commit 3eea688

Browse files
author
Vlad Balin
committed
Added more docs
1 parent 4d5d1e1 commit 3eea688

File tree

1 file changed

+146
-110
lines changed

1 file changed

+146
-110
lines changed

README.md

Lines changed: 146 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,171 +1,207 @@
11
# NestedReact
2-
This is React add-on providing advanced state management to React applications and convergence layer for intermixing React components and Backbone Views.
2+
3+
React application architecture with [classical OO models]() in the data layer.
34

45
Brief feature list:
56

6-
- Component's state management with [NestedTypes](https://github.com/Volicon/NestedTypes) model instead of React state.
7-
- Extended two-way data binding with -React- Nested links - [Guide to Data Binding Use Cases](/example/databinding.md)
8-
- Lightweight `NestedTypes`-style type annotations for props and context as a replacement of `PropTypes`.
9-
- *Pure render optimization* with mutable models and collections in props. Works like a charm :)
10-
- Transparent interoperation with existing Backbone Views:
11-
- React component can be used as backbone View. `new MyComponent.View({ props })`
12-
- Backbone Views can be used as React components. `<React.subview View={ MyView } />`
13-
- Simplified refactoring of Backbone Views to React components. `this.$`, `this.$el`, `this.$( sel )`, `this.model` works for React components too, as well as `this.trigger` and `this.listenTo`.
14-
15-
Though `NestedReact` offers excellent convergence layer for backbone views, raw backbone models are not supported.
16-
To use it for smooth migration of existing backbone application to React, you need to replace `backbone` with `NestedTypes`
17-
first (it's mostly backward compatible with backbone 1.2 by API, so transition is not hard).
18-
Which by itself will be a big step forward, because:
19-
- It's order of magnitude faster, so your application becomes more responsive and you can handle collection which are 10 times larger than you have now. [No kidding](http://slides.com/vladbalin/performance#/).
20-
- It implements nested models and collections handling in the right way. During `fetch`, nested objects are updated in place, so it's safe to pass them by reference.
21-
- It can handle model references by `id` in attributes for you too, operating on a set of independently fetched collections.
22-
- It's type-safe, providing the same contract for model attributes as in statically typed language. Thus,
23-
attributes are guaranteed to hold values of declared types whatever you do, making it impossible to break client-server protocol.
24-
- At the moment of writing, no other traditional model framework supports React's pure render optimization. :)
25-
26-
For more information about `NestedTypes`, visit
27-
http://volicon.github.io/backbone.nestedTypes/
28-
and
29-
https://github.com/Volicon/backbone.nestedTypes
7+
- First-class support for mutable models and collections in props, state, and context.
8+
- Unidirectional data flow and safe *pure render optimization*.
9+
- Two-way data binding ([Guide to Data Binding Use Cases](/example/databinding.md))
10+
- Optional local component subtree updates.
11+
- Lightweight type annotations for props, *state*, and context as a replacement for `PropTypes`.
12+
- Gradual transition procedure for backbone applications ([Backbone Migration Guide]()):
13+
- Complete interoperation with existing Backbone Views allowing you to reuse existing code and avoid upfront application rewrite.
14+
- Any type of application refactoring strategy is possible - top-to-bottom, bottom-to-top, and random parts at the middle.
15+
- Support for Backbone events and jQuery accessors in React components simplifies View refactoring.
16+
17+
Compare solution size and complexity to any of `flux` implementation on [TodoMVC example]().
3018

3119
# Installation
3220
It's packed as single UMD, thus grab the module or use `npm` to install.
3321
`npm install --save nestedreact`
3422

35-
Module extends React namespace (without touching original React), and its
23+
It has [NestedTypes model framework]() and [React]() as strong dependencies.
24+
25+
Module extends React namespace (without modifying original React), and its
3626
safe to use it as a replacement for `react`.
3727
`import React from 'nestedreact'`
3828

39-
If you're using backbone-based frameworks such as `ChaplinJS` or `Marionette`,
29+
If you're migrating from backbone-based frameworks such as `ChaplinJS` or `Marionette`,
4030
you need to do following things to make convergence layer work properly:
4131
- Make sure that frameworks includes `nestedtypes` instead of `backbone`.
4232
- On application start, tell `nestedreact` to use proper base class for the View.
4333
`React.useView( Chaplin.View )`
4434

45-
# Features
35+
# Basics
36+
## Managing component's state
4637

47-
## Managing state with ad-hoc Backbone model
38+
In the simplest case, it looks like this:
4839

4940
```javscript
50-
var React = require( 'nestedreact' );
51-
52-
var MyComponent = React.createClass({
53-
//Model : BackboneModel,
41+
import React from 'nestedreact'
5442
55-
state : { // Model defaults
56-
count : 0
43+
export const MyComponent = React.createClass({
44+
state : {
45+
count : 0 // Number attribute with 0 as default value.
5746
},
5847
59-
render : function(){
48+
render(){
6049
return (
6150
<div onClick={ this.onClick }>
6251
{ this.state.count }
6352
</div>
6453
);
6554
},
6655
67-
onClick : function(){
56+
onClick(){
57+
// state change will be detected and component will be updated
6858
this.state.count = this.state.count + 1;
6959
}
7060
});
7161
```
7262

73-
- New `NestedTypes` Model definition will be created, using `state` as Model.defaults.
74-
- If Model property is specified, it will be used as base model and extended.
75-
- `state` property from mixins will be properly merged.
76-
- Since `state` is `NestedTypes` model in this case,
77-
- All attributes *must* be declared using `NestedTypes` standard type specs.
78-
- `state` attributes allows direct assignments - treat it as regular object.
79-
- Every `state` modification (including direct assignments and nested attributes changes) will
80-
cause automagical react update.
63+
Behind the scene, `state` is managed with `NestedTypes` model which is implicitly created using
64+
attribute's spec taken from `state` declaration (refer to [NestedTypes documentation]() for complete
65+
attribute spec syntax). It has following implications:
8166

82-
## Passing Backbone objects as React components props
83-
```javscript
84-
var MyComponent = React.createClass({
85-
listenToProps : { // or just string with property names, separated by space
86-
model : 'change'
87-
},
67+
- You can use primitive type values or constructor functions as attribute's type specs.
68+
- Plain objects and arrays used as defaults will be properly deep copied.
69+
- All state members *must* be declared in `state`.
70+
- State attributes behaves as regular object attributes, which can be directly accessed and assigned.
71+
- State attributes can hold deeply nested models and collections; deep changes will be automatically detected and will cause component update.
8872

89-
render : function(){
90-
return (
91-
<div onClick={ this.onClick }>
92-
{ this.props.model.count }
93-
</div>
94-
);
95-
},
73+
In addition,
74+
- `state` property from mixins will be properly merged. So, mixins can have state too.
75+
- You can specify the base class for state model using `Model` component's property.
76+
- Entire model's state can be externally defined as `NestedTypes` Model, and attached to component by referencing it in `Model` property.
77+
- `state` supports everything what regular models can do, e.g. it can have custom methods,
78+
be transactionally changed, serialized, saved, fetched, etc.
9679

97-
onClick : function(){
98-
this.props.model.count = this.props.model.count + 1;
99-
}
80+
Usage of `getInitialState()` and `setState()` is not allowed when you're using `state` declaration.
81+
82+
## Passing models and collections as components props
83+
84+
It's quite common practice to describe complex application's page state in top-level component, and
85+
pass the parts of the state down as props. In this case, any changes to nested models
86+
and collections will be detected by top-level component and will cause update of the whole subtree.
87+
Resulting in so-called _unidirectional data flow_.
88+
89+
```javascript
90+
// data layer
91+
const Counter = Model.extend({
92+
attributes : {
93+
count : 0
94+
}
95+
});
96+
97+
// Application or application's page
98+
const Top = React.createClass({
99+
// all changes made to the parts of the state will cause component update
100+
state : {
101+
model1 : Counter,
102+
model2 : Counter
103+
},
104+
105+
render(){
106+
// pass down elements of the state...
107+
return (
108+
<div>
109+
<Bottom model={ this.state.model1 } />
110+
<Bottom model={ this.state.model2 } />
111+
</div>
112+
);
113+
}
100114
});
115+
116+
// Pure component. Click will trigger update of the Top component.
117+
const Bottom = ({ model }) => (
118+
<div onClick={ () => model.count += 1 }>
119+
{ model.count }
120+
</div>
121+
);
101122
```
102123

103-
You can update react component on backbone events from component props.
104-
Event subscription is managed automatically. No props passed - no problems.
124+
Also, this example demonstrates the point which really differentiate our approach to
125+
application state management. Here the simple fact comes into play - `NestedTypes` models
126+
can declaratively describe very complex state, and detect deeply nested changes.
127+
So, you have unidirectional data flow for no effort.
128+
129+
## Props specs and pure render optimization
130+
131+
One of the problems of unidirectional data flow is that large part of UI is being
132+
updated for every small change. Though React does its job avoiding unnecessary DOM manipulations,
133+
it's still takes a lot of computation resources to compare new and old UI components trees.
134+
`Pure render` optimization strategy avoids rendering and comparison of subtrees which has not been changed
135+
by adding special `props` comparison function (`shouldComponentUpdate`). This optimization
136+
is most effective on the top level close to the state holder component.
105137

106-
## NestedTypes-style props specs
138+
NestedReact support this optimization, comparing props model's and collection version tokens to ones used during the last render,
139+
and comparing other props values for strict equality.
140+
141+
To enable this optimization for the particular component, you need to:
142+
- Declare all props that will be tracked for changes in `props` (or `propTypes`) spec.
143+
Which is the good practice by itself, so you encouraged to do it unless you're using
144+
stateless function components syntax, which is preferable.
145+
- Add `pureRender : true` to component definition.
146+
147+
As from previous example:
107148

108149
```javscript
109-
var MyComponent = React.createClass({
150+
var Bottom = React.createClass({
110151
props : {
111-
model : MyFancyModel
152+
model : Counter
112153
},
113-
114-
listenToProps : { // or just string with property names, separated by space
115-
model : 'change'
116-
},
117-
118-
render : function(){
119-
return (
120-
<div onClick={ this.onClick }>
121-
{ this.props.model.count }
122-
</div>
123-
);
124-
},
125154
126-
onClick : function(){
127-
this.props.model.count = this.props.model.count + 1;
128-
}
155+
pureRender : true,
156+
157+
render(){
158+
const { model } = this.props;
159+
return (
160+
<div onClick={ () => model.count += 1 }>
161+
{ model.count }
162+
</div>
163+
);
164+
)
129165
});
130166
```
131167

132-
Simplified NestedTypes-style type annotations can be used as props spec:
133-
- constructor functions: `Type`
134-
- constructors with default values: `Type.value( x )`
135-
- JSON and primitive values: `"default string"`
168+
NestedReact `props` spec uses the simple subset of `state` spec, and acts as substitution for `propTypes` (in fact,
169+
it internally compiles itself to the `propTypes` and `getDefaultProps()`).
170+
171+
Following type annotations are allowed for `props`:
172+
1. Constructor functions: `prop1 : String`
173+
2. Constructors with default value: `prop2 : String.value( "default string" )`
174+
3. JSON and primitive values: `prop3 : "default string"`
175+
4. Special PropTypes cases:
176+
- `PropTypes.any` -> `undefined` (no default value) or `null` (with `null` default value)
177+
- `PropTypes.node` -> `React.Node`
178+
- `PropTypes.element` -> `React.Element`
179+
180+
If prop has explicitly declared default value, as in (2) or (3), it will be added to `getDefaultProps`.
136181

137-
No other type annotation features are supported for `props`.
182+
## Partial component subtree updates
138183

139-
When component has `props` type spec:
140-
- React component propTypes will be automatically generated for every props;
141-
- if props has explicitly defined default value, getDefaultProps() method will be created. It means, that there are *no*
142-
default objects generated for simple `Type` style type spec.
184+
For the number of reasons, you may need some parts of components subtrees to listen for props updates independently.
185+
It might be required if model or collection props are not the part of any upper component state.
186+
This situation frequently happens during transition period when you're in the middle of refactoring large
187+
backbone application.
143188

144-
## Pure Render Mixin
189+
To make `<Bottom />` component listen to and update on its `model` prop change, it's enough to add
190+
`listenToProps` option to component spec. It will play well with `pureRender`, effectively
191+
avoiding unnecessary renders if top level component will trigger update on the same change too.
145192

146193
```javscript
147-
var MyComponent = React.createClass({
148-
props : {
149-
item : MyModel,
150-
elements : MyCollection,
151-
className : String
152-
},
153-
154-
pureRender : true,
155-
156-
render : function(){
157-
return (
158-
...
159-
);
160-
}
194+
var Bottom = React.createClass({
195+
props : {
196+
model : Counter
197+
},
198+
199+
listenToProps : 'model', // space separated list of prop names
200+
201+
// ...all other stays the same...
161202
});
162203
```
163204

164-
PureRender optimization in enabled with `pureRender` option. It will create `shouldComponentUpdate` function
165-
which is optimized for props mentioned in `propTypes` or `props` declaration.
166-
167-
Therefore, it's required to declare all of component props when using this optimization.
168-
169205
## Data binding
170206

171207
`nestedreact` supports data binding links compatible with standard React's `valueLink`.

0 commit comments

Comments
 (0)