Skip to content

Commit 66d9c65

Browse files
author
Vlad Balin
committed
Merge pull request #4 from Volicon/develop
Version 0.3.0
2 parents 95b4f59 + 0af701e commit 66d9c65

21 files changed

+1440
-843
lines changed

README.md

Lines changed: 129 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,61 @@
1-
# react-backbone.glue
2-
This is React add-on designed to replace Backbone views with React in existing Backbone application.
1+
# nestedreact
2+
This is React add-on designed to simplify migration to React views in large Backbone applications.
33

44
It allows you:
55

6-
- To use React component in place of every Backbone View in your application.
7-
- To use your existing Backbone Views as subviews in React components.
6+
- To use React component in place of every Backbone View.
7+
- To use your existing Backbone Views from React components.
88
- To use your existing Backbone Models as React component state.
99
- Update React components on Backbone events.
10+
- Data-binding for models and collections
1011

1112
Thus, no refactoring of your application is required. You can start writing UI with React immediately replacing your Backbone Views one-by-one, while keeping your existing models.
1213

13-
This extension works with raw Backbone. However, in order to take full advantage of React/Backbone
14-
architecture you are encouraged to upgrade to `Backbone.NestedTypes`. It will give you following
14+
# Breaking changes introduced in 0.3
15+
- `component.createView( props )` doesn't work any more, use `new component.View( props )` instead.
16+
- module and `npm` package name is now `nestedreact`.
17+
- Raw `backbone` is not supported any more. Upgrade to `NestedTypes` 1.1.5 or more is required. It will give you following
1518
features to help managing complex application state:
19+
- Proper nested models and collections implementation with deep changes detection. React components will
20+
update UI on nested attribute changes.
21+
- Dramatic improvement in model update performance compared to Backbone. Up to 40x faster in Chrome. Important for mobile devices.
22+
- Type safety. Attributes are guaranteed to hold values of declared types all the time.
1623

17-
- Proper nested models and collections implementation with deep changes detection. React components will
18-
update UI on nested attribute changes.
19-
- Dramatic improvement in model update performance compared to Backbone. Up to 40x faster in Chrome. Imprortant for mobile devices.
20-
- Type safety. Attributes are guaranteed to hold values of declared types all the time.
21-
22-
For more information, visit
23-
http://volicon.github.io/backbone.nestedTypes/
24-
and
25-
https://github.com/Volicon/backbone.nestedTypes
24+
For more information about `NestedTypes`, visit
25+
http://volicon.github.io/backbone.nestedTypes/
26+
and
27+
https://github.com/Volicon/backbone.nestedTypes
2628

2729
# Usage
28-
It's packed as single UMD, thus grab the module which is appropriate for you. So far, there are two of them, one for raw backbone, and one for `backbone.nestedTypes`.
30+
It's packed as single UMD, thus grab the module or use `npm` to install.
31+
`npm install --save nestedreact`
2932

30-
First one depends on `react` and `backbone`, so if you're using Chaplin or Marionette you will
31-
probably need to pass appropriate module instead of `backbone`. Don't hesitate to replace module name in the beginning of the file, or use raw factory function from `src/glue.js`.
33+
Module export's modified React namespace (without touching original React), and its
34+
safe to use it as a replacement for `react`.
3235

33-
If you're using `NestedTypes`, you need `NestedTypes` to require appropriate framework. Report a bug if something goes wrong, we like when someone share our passion for technology and quite fast in response.
36+
If you're using backbone-based frameworks such as `ChaplinJS` or `Marionette`,
37+
you need to do following things:
38+
- Make sure that frameworks includes `nestedtypes` instead of `backbone`.
39+
- On application start, tell `nestedreact` to use proper base class for View.
40+
`require( 'nestedreact' ).useView( Chaplin.View )`
3441

3542
# Features
3643
## Use React components as Backbone View
3744

3845
```javscript
39-
var backboneView = MyReactComponent.createView( props );
46+
var backboneView = new MyReactComponent.View( props );
4047
```
4148

4249
## Use Backbone View in React component
4350

4451
```javscript
52+
var React = require( 'nestedreact' );
53+
4554
var MyComponent = React.createClass({
4655
render : function(){
4756
return (
4857
<div>
49-
<React.subview
58+
<React.subview
5059
className="classes for root element"
5160
View={ BackboneView }
5261
options={ viewOptions }
@@ -57,37 +66,64 @@ var MyComponent = React.createClass({
5766
});
5867
```
5968

69+
## Helper methods for easy Backbone to React transition
70+
71+
There are `el`, `$el`, and `$( selector )` available for the React components,
72+
which simplifies refactoring of the existing event handlers and usage of
73+
`jquery` plugins.
74+
75+
```javscript
76+
var React = require( 'nestedreact' );
77+
78+
var MyComponent = React.createClass({
79+
onClick : function(){
80+
this.$( '#somewhere' ).html( 'Hi' );
81+
}
82+
});
83+
```
84+
85+
It is extremely dangerous and conceptually wrong to directly *modify existing*
86+
DOM subtree in React component. Read is safe, modify DOM when you know what you're
87+
doing. Lets say, integrating `jQuery` plugins.
88+
89+
*You must not use these methods in render*. `jquery` plugins can be initialized
90+
in `componentDidMount` method or in event handlers.
91+
6092
## Use Existing Backbone Model as component's state
6193

6294
```javscript
95+
var React = require( 'nestedreact' );
96+
6397
var MyComponent = React.createClass({
6498
Model : BackboneModel,
6599
66100
render : function(){
67101
return (
68102
<div onClick={ this.onClick }>
69-
{ this.state.get( 'count' ) }
103+
{ this.state.count }
70104
</div>
71105
);
72106
},
73107
74108
onClick : function(){
75-
this.state.set( 'count', this.state.get( 'count' ) + 1 );
109+
this.state.count = this.state.count + 1;
76110
}
77111
});
78112
```
79113

80114
If Model is specified for the component,
81-
- `this.state` is backbone model. Usage of `setState` is not allowed.
115+
- `this.state` and `this.model` holds backbone model. Usage of `setState` is *not allowed*.
82116
- React component will update itself whenever model emit `change` event.
83117
- You can customize UI update events supplying `listenToState` property. For example, `listenToState : 'change:attr sync'`.
84118
- You can disable UI updates on state change, supplying `listenToState : false` option.
85119

86120
## Managing state with ad-hoc Backbone model
87121

88122
```javscript
123+
var React = require( 'nestedreact' );
124+
89125
var MyComponent = React.createClass({
90-
//Model : BackboneModel,
126+
//Model : BackboneModel,
91127
92128
attributes : { // Model defaults
93129
count : 0
@@ -96,43 +132,103 @@ var MyComponent = React.createClass({
96132
render : function(){
97133
return (
98134
<div onClick={ this.onClick }>
99-
{ this.state.get( 'count' ) }
135+
{ this.state.count }
100136
</div>
101137
);
102138
},
103139
104140
onClick : function(){
105-
this.state.set( 'count', this.state.get( 'count' ) + 1 );
141+
this.state.count = this.state.count + 1;
106142
}
107143
});
108144
```
109145

110-
- New Model definition will be created, using `attributes` as Model.defaults.
146+
- New `NestedTypes` Model definition will be created, using `attributes` as Model.defaults.
111147
- If Model property is specified, it will be used as base model and extended.
112148
- `attributes` property from mixins will be properly merged.
113-
- if you're using `Backbone.NestedTypes` models, it's far superior to react state in every aspect. It handles updates much faster, it detects nested elements changes, and it has type specs for state elements in a way like react's `propTypes`.
149+
- Since `state` is `NestedTypes` model in this case,
150+
- All attributes *must* be declared using `NestedTypes` standard type specs.
151+
- `state` attributes allows direct assignments - treat it as regular object.
152+
- Every `state` modification (including direct assignments and nested attributes changes) will
153+
cause automagical react update.
114154

115155
## Passing Backbone objects as React components props
116156
```javscript
117157
var MyComponent = React.createClass({
118-
listenToProps : {
158+
listenToProps : { // or just string with property names, separated by space
119159
model : 'change'
120160
},
121161
122162
render : function(){
123163
return (
124164
<div onClick={ this.onClick }>
125-
{ this.props.model.get( 'count' ) }
165+
{ this.props.model.count }
126166
</div>
127167
);
128168
},
129169
130170
onClick : function(){
131-
this.props.model.set( 'count', this.props.model.get( 'count' ) + 1 );
171+
this.props.model.count = this.props.model.count + 1;
132172
}
133173
});
134174
```
135175

136176
You can update react component on backbone events from component props.
137-
138177
Event subscription is managed automatically. No props passed - no problems.
178+
179+
## Data binding
180+
181+
`nestedreact` supports data binding links compatible with standard React's `valueLink`.
182+
Links are "live" in a sense that they always point to actual value based on current model or collection state.
183+
It doesn't break anything in React, rather extends possible use cases.
184+
185+
- `var link = model.getLink( 'attr' )` creates link for model attribute.
186+
- `var link = collection.getLink( model )` creates boolean link, toggling model in collection. True if model is contained in collection, assignments will add/remove given model. Useful for checkboxes.
187+
188+
### Value access methods
189+
190+
In addition to standard members `link.requestChange( x )` and `link.value`, links supports all popular property access styles:
191+
192+
- jQuery property style: setter `link.val( x )`, getter `link.val()`
193+
- Backbone style: setter `link.set( x )`, getter `link.get()`
194+
- plain assugnments style: setter `link.value = x`, getter `link.value`
195+
- `link.toggle()` is a shortcut for `link.requestChange( !link.value )`
196+
197+
Most efficient way to work with link is using `link.val()` function, that's how its internally implemented. `val` function is bound, and can be passed around safely.
198+
199+
### Link transformations
200+
201+
Attribute's link can be further transformed using extended link API. Link transformations allowing you to use new `stateless functions` component definition style introduced in React 0.14 in most cases.
202+
203+
For links with any value type:
204+
205+
- `link.equals( x )` creates boolean link which is true whenever link value is equal to x. Useful for radio groups.
206+
- `link.update( x => !x )` creates function transforming link value (toggling boolean value in this case). Useful for `onClick` event handlers.
207+
208+
For link enclosing array:
209+
210+
- `arrLink.contains( x )` creates boolean link which is true whenever x is contained in an array in link value. Useful for checkboxes. Avoid long arrays, currently operations has O(N^2) complexity.
211+
212+
For link enclosings arrays and plain JS objects:
213+
- `arrOrObjLink.at( key )` creates link to array of object member with a given key. Can be applied multiple times to work with object hierarchies; on modifications, objects will be updated in purely functional way (modified parts will be shallow copied). Useful when working with plain JS objects in model attributes - updating them through links make changes visible to the model.
214+
- `arrOrObjLink.map( ( itemLink, key ) => <input key={ key } valieLink={ itemLink } /> )` iterates through object or array, wrapping its elements to links. Useful for JSX transofrmation.
215+
216+
### Links and components state
217+
218+
Link received through component props can be mapped as state member using the following declaration:
219+
```javascript
220+
attributes : {
221+
selected : Nested.link( '^props.selectedLink' )
222+
}
223+
```
224+
It can be accessed as a part of state, however, in this case it's not true state. All read/write operations will be done with link itself, and local state won't be modified.
225+
226+
Also, links can be used to declaratively expose real component state to upper conponents. In this example, link optionally received in props will be updated every time `this.state.selected` object is replaced. In this case, updates are one way, from bottom component to upper one, and stateful component will render itself when state is changed.
227+
228+
```javascript
229+
attributes : {
230+
selected : Item.has.watcher( '^props.selectedLink.val' )
231+
}
232+
```
233+
234+
Technically, "watcher" - is just a callback function with a single argument receiving new attribute value, so links are not required here.

example/test.html

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<html>
2+
<head>
3+
<script src="../node_modules/jquery/dist/jquery.js"></script>
4+
<script src="../node_modules/underscore/underscore.js"></script>
5+
<script src="../node_modules/nestedtypes/nestedtypes.js"></script>
6+
<script src="../node_modules/react/dist/react.js"></script>
7+
<script src="../node_modules/react-dom/dist/react-dom.js"></script>
8+
<script src="../nestedreact.js"></script>
9+
10+
</head>
11+
<body></body>
12+
<script>
13+
var State = Nested.Model.extend({
14+
defaults : {
15+
who : 'me'
16+
}
17+
});
18+
19+
var Hello = React.createClass({
20+
Model : State,
21+
22+
attributes : {
23+
what : 'hi'
24+
},
25+
26+
render : function(){
27+
return React.createElement( 'div', { onClick : this.click }, this.model.what + this.model.who );
28+
},
29+
30+
click : function(){
31+
this.$el.html( 'Whoaa!' );
32+
}
33+
});
34+
35+
var c = ReactDOM.render( React.createElement( Hello ), document.body );
36+
c.state.set( 'who', 'he' );
37+
38+
</script>
39+
</html>

0 commit comments

Comments
 (0)