|
| 1 | +# nestedreact |
| 2 | +This is React add-on designed to simplify migration to React views in large Backbone applications. |
| 3 | + |
| 4 | +It allows you: |
| 5 | + |
| 6 | +- To use React component in place of every Backbone View. |
| 7 | +- To use your existing Backbone Views from React components. |
| 8 | +- To use your existing Backbone Models as React component state. |
| 9 | +- Update React components on Backbone events. |
| 10 | +- Data-binding for models and collections |
| 11 | + |
| 12 | +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. |
| 13 | + |
| 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 |
| 18 | +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. |
| 23 | + |
| 24 | + For more information about `NestedTypes`, visit |
| 25 | + http://volicon.github.io/backbone.nestedTypes/ |
| 26 | + and |
| 27 | + https://github.com/Volicon/backbone.nestedTypes |
| 28 | + |
| 29 | +# Usage |
| 30 | +It's packed as single UMD, thus grab the module or use `npm` to install. |
| 31 | + `npm install --save nestedreact` |
| 32 | + |
| 33 | +Module export's modified React namespace (without touching original React), and its |
| 34 | +safe to use it as a replacement for `react`. |
| 35 | + |
| 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 )` |
| 41 | + |
| 42 | +# Features |
| 43 | +## Use React components as Backbone View |
| 44 | + |
| 45 | +```javscript |
| 46 | +var backboneView = new MyReactComponent.View( props ); |
| 47 | +``` |
| 48 | + |
| 49 | +## Use Backbone View in React component |
| 50 | + |
| 51 | +```javscript |
| 52 | +var React = require( 'nestedreact' ); |
| 53 | + |
| 54 | +var MyComponent = React.createClass({ |
| 55 | + render : function(){ |
| 56 | + return ( |
| 57 | + <div> |
| 58 | + <React.subview |
| 59 | + className="classes for root element" |
| 60 | + View={ BackboneView } |
| 61 | + options={ viewOptions } |
| 62 | + /> |
| 63 | + </div> |
| 64 | + ); |
| 65 | + } |
| 66 | +}); |
| 67 | +``` |
| 68 | + |
| 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 | + |
| 92 | +## Use Existing Backbone Model as component's state |
| 93 | + |
| 94 | +```javscript |
| 95 | +var React = require( 'nestedreact' ); |
| 96 | + |
| 97 | +var MyComponent = React.createClass({ |
| 98 | + Model : BackboneModel, |
| 99 | + |
| 100 | + render : function(){ |
| 101 | + return ( |
| 102 | + <div onClick={ this.onClick }> |
| 103 | + { this.state.count } |
| 104 | + </div> |
| 105 | + ); |
| 106 | + }, |
| 107 | + |
| 108 | + onClick : function(){ |
| 109 | + this.state.count = this.state.count + 1; |
| 110 | + } |
| 111 | +}); |
| 112 | +``` |
| 113 | + |
| 114 | +If Model is specified for the component, |
| 115 | +- `this.state` and `this.model` holds backbone model. Usage of `setState` is *not allowed*. |
| 116 | +- React component will update itself whenever model emit `change` event. |
| 117 | + - You can customize UI update events supplying `listenToState` property. For example, `listenToState : 'change:attr sync'`. |
| 118 | + - You can disable UI updates on state change, supplying `listenToState : false` option. |
| 119 | + |
| 120 | +## Managing state with ad-hoc Backbone model |
| 121 | + |
| 122 | +```javscript |
| 123 | +var React = require( 'nestedreact' ); |
| 124 | + |
| 125 | +var MyComponent = React.createClass({ |
| 126 | + //Model : BackboneModel, |
| 127 | + |
| 128 | + attributes : { // Model defaults |
| 129 | + count : 0 |
| 130 | + }, |
| 131 | + |
| 132 | + render : function(){ |
| 133 | + return ( |
| 134 | + <div onClick={ this.onClick }> |
| 135 | + { this.state.count } |
| 136 | + </div> |
| 137 | + ); |
| 138 | + }, |
| 139 | + |
| 140 | + onClick : function(){ |
| 141 | + this.state.count = this.state.count + 1; |
| 142 | + } |
| 143 | +}); |
| 144 | +``` |
| 145 | + |
| 146 | +- New `NestedTypes` Model definition will be created, using `attributes` as Model.defaults. |
| 147 | +- If Model property is specified, it will be used as base model and extended. |
| 148 | +- `attributes` property from mixins will be properly merged. |
| 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. |
| 154 | + |
| 155 | +## Passing Backbone objects as React components props |
| 156 | +```javscript |
| 157 | +var MyComponent = React.createClass({ |
| 158 | + listenToProps : { // or just string with property names, separated by space |
| 159 | + model : 'change' |
| 160 | + }, |
| 161 | + |
| 162 | + render : function(){ |
| 163 | + return ( |
| 164 | + <div onClick={ this.onClick }> |
| 165 | + { this.props.model.count } |
| 166 | + </div> |
| 167 | + ); |
| 168 | + }, |
| 169 | + |
| 170 | + onClick : function(){ |
| 171 | + this.props.model.count = this.props.model.count + 1; |
| 172 | + } |
| 173 | +}); |
| 174 | +``` |
| 175 | + |
| 176 | +You can update react component on backbone events from component props. |
| 177 | +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 | +<<<<<<< HEAD |
| 225 | +It can be accessed as a part of state, however, `link.requestChanges` will be call on assignment |
| 226 | +instead of state modification. Its value will be updated automatically when component will receive new props. |
| 227 | + |
| 228 | +[Guide to Data Binding Use Cases](/example/databinding.md) |
| 229 | +======= |
| 230 | +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. |
| 231 | + |
| 232 | +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. |
| 233 | + |
| 234 | +```javascript |
| 235 | +attributes : { |
| 236 | + selected : Item.has.watcher( '^props.selectedLink.val' ) |
| 237 | +} |
| 238 | +``` |
| 239 | + |
| 240 | +Technically, "watcher" - is just a callback function with a single argument receiving new attribute value, so links are not required here. |
| 241 | +>>>>>>> origin/develop |
0 commit comments