|
| 1 | +# Backbone to React Transition Guide |
| 2 | + |
| 3 | +There are many different ways you may approach the problem when dealing with existing Bakcbone UI. |
| 4 | +All of them are supported, enabling easy and gradual transition to React. |
| 5 | + |
| 6 | +## Interoperation with existing Backbone Views |
| 7 | + |
| 8 | +Key factor of success for technology transition project is to avoid naive 'upfront rewrite' strategy. |
| 9 | +An ability of running old and new code together is the game changer, allowing you to make transition process gradual. |
| 10 | +This strategy has a number of benefits, and probably one of the most significant is that you don't need to stop |
| 11 | +delivering new features. |
| 12 | + |
| 13 | +### Use React components as Backbone View |
| 14 | + |
| 15 | +When you work on new features, it's natural to decide that you will write its UI with React. |
| 16 | + |
| 17 | +And, there're good news. Any React component may be used as Backbone subview. As easy as that: |
| 18 | + |
| 19 | +```javscript |
| 20 | +var backboneView = new MyReactComponent.View( props ); |
| 21 | +``` |
| 22 | + |
| 23 | +It will also enable you to start refactoring of your application from the bottom to top, |
| 24 | +dealing with small isolated parts of the code first. |
| 25 | + |
| 26 | +### Use Backbone View in React component |
| 27 | + |
| 28 | +Or, you can decide to start refactoring from the top. In this case, you will likely want to reuse |
| 29 | + your existing lower-level backbone subviews. |
| 30 | + |
| 31 | +You can do it like that. And it will work okay. |
| 32 | + |
| 33 | +```javscript |
| 34 | +var React = require( 'nestedreact' ); |
| 35 | +
|
| 36 | +var MyComponent = React.createClass({ |
| 37 | + render : function(){ |
| 38 | + return ( |
| 39 | + <div> |
| 40 | + <React.subview |
| 41 | + className="classes for root element" |
| 42 | + View={ BackboneView } |
| 43 | + options={ viewOptions } |
| 44 | + /> |
| 45 | + </div> |
| 46 | + ); |
| 47 | + } |
| 48 | +}); |
| 49 | +``` |
| 50 | + |
| 51 | +Taking these two features together, you can take literally any view from the subview hierarchy, and replace it with |
| 52 | + React component. It will also work fine if there are multiple layers - React using Backbone using React... |
| 53 | + |
| 54 | +## Backbone View refactoring |
| 55 | + |
| 56 | +Occasionally, you may decide to refactor your existing View to React component. |
| 57 | + |
| 58 | +Since Backbone generally use the same architectural concept as React (detect change and then render), it's an easy process. |
| 59 | + First of all, short vocabulary: |
| 60 | + 1. View.extend({}) -> React.createClass({}). That's an obvious part. |
| 61 | + 2. View.template -> Component.render(). Yeah. In React, `render` function just *returns* markup. |
| 62 | + 3. View.render -> Component.forceUpdate(). And if you want to update component, you should call this thingy instead. |
| 63 | + 4. View.render -> Component.componentDidUpdate(), Component.componentDidMount(). If you want to attach jQuery plugin after render, you do it here. |
| 64 | + 5. View.initialize( options ) -> View.componentWillMount() |
| 65 | + 6. View options you receive in (4) -> Component.props |
| 66 | + 7. View.model -> Component.state |
| 67 | + |
| 68 | +You approach the refactoring process in sequence: |
| 69 | + 1. Create an empty React component. |
| 70 | + 2. Take your View's template, and convert it to JSX in your component's render method. |
| 71 | + 3. Your View's `options` become component's `props`. Modify `render` function accordingly. |
| 72 | + 4. And then, the `model` of your View becomes your component's `state`. Modify `render` function accordingly. |
| 73 | + 5. You copy all of your event handlers. |
| 74 | + |
| 75 | +Keep in mind - in React direct DOM manipulation is not allowed. Thus, you must render on every change, and `props` + `state` |
| 76 | +must completely define an appearance of your markup. Since for Backbone it's not so, you will likely be required to expand your |
| 77 | +View's state model. |
| 78 | + |
| 79 | +In Backbone, you might assign values from `options` to the model. Do not do this with React. Remember, `options` is `props`. |
| 80 | +Therefore, it might be required to remove some items from the View's model. |
| 81 | + |
| 82 | +### Use Existing Backbone Model as component's state |
| 83 | + |
| 84 | +If you already have one model, describing View's state (usual pattern is listening to model's `change` event and calling `this.render()`), |
| 85 | + you can just attach it to you React component. Like this. It will be created, disposed, and listened to automatically. |
| 86 | + |
| 87 | +```javscript |
| 88 | +var React = require( 'nestedreact' ); |
| 89 | +
|
| 90 | +var MyComponent = React.createClass({ |
| 91 | + Model : MyStateModel, |
| 92 | +
|
| 93 | + render : function(){ |
| 94 | + return ( |
| 95 | + <div onClick={ this.onClick }> |
| 96 | + { this.state.count } |
| 97 | + </div> |
| 98 | + ); |
| 99 | + }, |
| 100 | +
|
| 101 | + onClick : function(){ |
| 102 | + this.state.count = this.state.count + 1; |
| 103 | + } |
| 104 | +}); |
| 105 | +``` |
| 106 | + |
| 107 | +*Please, note.* If Model is specified for the component, |
| 108 | +- `this.state` and `this.model` variables holds backbone model. Usage of `setState` is *not allowed*. Generally, NestedTypes |
| 109 | + models are far superior to React's state in its capabilities, so trust me, there are nothing to regret. |
| 110 | +- React component will update itself automatically whenever model emit `change` event. |
| 111 | + - You can customize UI update events supplying `listenToState` property. For example, `listenToState : 'change:attr sync'`. |
| 112 | + - You can disable UI updates on state change, supplying `listenToState : false` option. |
| 113 | + |
| 114 | +## Passing Backbone objects as React components props |
| 115 | + |
| 116 | +In backbone, you might listen to models and collection changes which comes from the View `options`. |
| 117 | + |
| 118 | +You can do it manually in React keeping in mind that `componentWIllMount` is substitution for `initialize`, but it's |
| 119 | +not that simple because React component's lifecycle is more complicated. In contrast with Views, components are able to |
| 120 | + receive props updates. Thus, you need to handle it, and it might become tricky. |
| 121 | + |
| 122 | +To address this problem, there's special declarative syntax for events subscription from `props`. |
| 123 | + |
| 124 | +```javscript |
| 125 | +var MyComponent = React.createClass({ |
| 126 | + listenToProps : 'model', // listen to change, and render |
| 127 | + /* |
| 128 | + listenToProps : { // or just string with property names, separated by space |
| 129 | + model : 'change' //listen to event names separated by space, and render |
| 130 | + }, |
| 131 | + or |
| 132 | + listenToProps : { // ...if you want really wierd things... |
| 133 | + model : { |
| 134 | + 'change' : function(){ |
| 135 | + // ...you may do it. But here we are just listening to 'change', and render. |
| 136 | + this.forceUpdate(); |
| 137 | + } |
| 138 | + } |
| 139 | + }, |
| 140 | + */ |
| 141 | + |
| 142 | + render : function(){ |
| 143 | + return ( |
| 144 | + <div onClick={ this.onClick }> |
| 145 | + { this.props.model.count } |
| 146 | + </div> |
| 147 | + ); |
| 148 | + }, |
| 149 | +
|
| 150 | + onClick : function(){ |
| 151 | + this.props.model.count = this.props.model.count + 1; |
| 152 | + } |
| 153 | +}); |
| 154 | +``` |
| 155 | + |
| 156 | +That's simple and safe. No props passed - no events subscription. |
| 157 | + |
| 158 | +## Helper methods for easy Backbone to React refactoring |
| 159 | + |
| 160 | +When you will copy over your event handlers, most likely, they will just work. |
| 161 | + |
| 162 | +There are `el`, `$el`, and `$( selector )` available for the React components, |
| 163 | +which simplifies refactoring of the existing event handlers and usage of |
| 164 | +`jquery` plugins. |
| 165 | + |
| 166 | +```javscript |
| 167 | +var React = require( 'nestedreact' ); |
| 168 | +
|
| 169 | +var MyComponent = React.createClass({ |
| 170 | + onClick : function(){ |
| 171 | + this.$( '#somewhere' ).html( 'Hi' ); |
| 172 | + } |
| 173 | +}); |
| 174 | +``` |
| 175 | + |
| 176 | +If they don't do DOM manipulation, which is prohibited. Instead, event handlers should modify the state, or call some callbacks |
| 177 | + received from props. |
| 178 | + |
| 179 | +It is extremely dangerous and conceptually wrong to directly *modify existing* |
| 180 | +DOM subtree in React component. Read is safe, modify DOM when you know what you're |
| 181 | +doing. Lets say, integrating `jQuery` plugins. |
| 182 | + |
| 183 | +*You must not use these methods in render*. `jquery` plugins can be initialized |
| 184 | + in `componentDidMount` method or in event handlers. |
| 185 | + |
0 commit comments