Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Jamis Charles, PayPal, San Jose — Migrating Safely to React

Jamis Charles, PayPal, San Jose — Migrating Safely to React

Jamis Charles, PayPal, San Jose — Migrating Safely to React

You love React. You want to rewrite your whole app in React right now. But you have millions of paying users, and maintaining a stable app is absolutely critical. How do you migrate now and optimize for future change?

React Amsterdam

April 20, 2016
Tweet

More Decks by React Amsterdam

Other Decks in Technology

Transcript

  1. • too many abstraction layers • low confidence • difficult

    state bugs (currency codes mismatched etc) Pain
  2. • V1: Just ship it • Maintainability is low priority

    • Complexity rises What happened?
  3. • React simplifies the view (fewer files) • Quick win

    • Push it to prod • Learn & evaluate Lessons learned
  4. • Backbone Router + custom routing logic • complex •

    difficult to track url -> code • good opportunity Routing Layer
  5. require('backboneSubroute'); module.exports = Backbone.SubRoute.extend({ showView: function (options) { if (options.name)

    { this.loadScripts(options); } historyModel.addPath(options.name); }, getPath: function (scriptName) { // getPath code }, loadScripts: function (options) { // loadScripts code }, cleanView: function () { // cleanView code } });
  6. // React Components... import MainContainer from '../containers/main'; import RequestContainer from

    '../containers/request'; import SendContainer from '../containers/send'; import BuyContainer from '../containers/buy'; ReactDOM.render( <Router history={history}> <Route path='/myaccount/transfer/' component={MainContainer}> {/* Flow entry points */} <Route path={'request(/:action)'} component={RequestContainer} /> <Route path={'send(/:action)'} component={SendContainer} /> <Route path={'buy(/:action)'} component={BuyContainer} /> </Route> </Router>, document.getElementById('react-transfer-container'));
  7. import React from 'react'; import indexView from '../index'; let SendContainer

    = React.createClass({ propTypes: { params: React.PropTypes.object.isRequired }, componentDidMount() { this.showBBView(); }, componentDidUpdate() { this.showBBView(); }, // will load and show a Backbone view. showBBView() { let type = 'send'; let action = this.props.params.action || 'recipient'; // gets content and renders it into the DOM. indexView.navigate(type, action); }, render() { return null; } }); export default SendContainer;
  8. import React from 'react'; import indexView from '../index'; let SendContainer

    = React.createClass({ propTypes: { params: React.PropTypes.object.isRequired }, componentDidMount() { this.showBBView(); }, componentDidUpdate() { this.showBBView(); }, // will load and show a Backbone view. showBBView() { let type = 'send'; let action = this.props.params.action || 'recipient'; // gets content and renders it into the DOM. indexView.navigate(type, action); }, render() { return null; } }); export default SendContainer;
  9. import React from 'react'; import indexView from '../index'; let SendContainer

    = React.createClass({ propTypes: { params: React.PropTypes.object.isRequired }, componentDidMount() { this.showBBView(); }, componentDidUpdate() { this.showBBView(); }, // will load and show a Backbone view. showBBView() { let type = 'send'; let action = this.props.params.action || 'recipient'; // gets content and renders it into the DOM. indexView.navigate(type, action); }, render() { return null; } }); export default SendContainer;
  10. import React from 'react'; import indexView from '../index'; let SendContainer

    = React.createClass({ propTypes: { params: React.PropTypes.object.isRequired }, componentDidMount() { this.showBBView(); }, componentDidUpdate() { this.showBBView(); }, // will load and show a Backbone view. showBBView() { let type = 'send'; let action = this.props.params.action || 'recipient'; // gets content and renders it into the DOM. indexView.navigate(type, action); }, render() { return null; } }); export default SendContainer;
  11. import React from 'react'; import * as routeUtil from '../utils/routeHelper';

    let SendContainer = React.createClass({ propTypes: { params: React.PropTypes.object.isRequired }, componentDidMount () { this.showBBView(); }, componentDidUpdate() { this.showBBView(); }, // will load and show a Backbone view. showBBView() { let type = 'send'; let action = this.props.params.action || 'recipient'; // gets content and renders it into the DOM. routeUtil.getIndexView().navigate(type, action); }, render() { return null; } }); export default SendContainer;
  12. import React from 'react'; import * as routeUtil from '../utils/routeHelper';

    let SendContainer = React.createClass({ propTypes: { params: React.PropTypes.object.isRequired }, render() { return <SendComponent /> } }); export default SendContainer;
  13. • Url → code is easy now • Reduced complexity

    • Good migration path for a give url Routing Layer
  14. • Backbone Models & Collections • complex • Most of

    our state pain comes from here Data Layer
  15. import $ from ‘jquery'; import Backbone from ‘backbone'; export default

    Backbone.View.extend({ el: 'form', events: { 'keyup #note': 'resizeTextAreaField', 'blur #note': 'hideTextAreaCounters' }, // Resize text area as user types resizeTextAreaField: function (event) { var count = this.countTextContent(); var newHeight = this.getIdealTextAreaHeight(count); var newRows = this.getRowsFromHeight(newHeight); $('#note').height(newHeight).attr('rows', newRows); }, // Update textarea counter with remaining character left hideTextAreaCounters: function (event) { // pseudo code },
  16. import $ from ‘jquery'; import Backbone from ‘backbone'; export default

    Backbone.View.extend({ el: 'form', events: { 'keyup #note': 'resizeTextAreaField', 'blur #note': 'hideTextAreaCounters' }, // Resize text area as user types resizeTextAreaField: function (event) { var count = this.countTextContent(); var newHeight = this.getIdealTextAreaHeight(count); var newRows = this.getRowsFromHeight(newHeight); $('#note').height(newHeight).attr('rows', newRows); }, // Update textarea counter with remaining character left hideTextAreaCounters: function (event) { // pseudo code },
  17. import $ from ‘jquery'; import Backbone from ‘backbone'; import *

    as ResizeUtil from 'utils/resizeTextareaUtil'; export default Backbone.View.extend({ el: 'form', events: { 'keyup #note': 'resizeTextAreaField', 'blur #note': 'hideTextAreaCounters' }, // Resize text area as user types resizeTextAreaField: ResizeUtil.resizeField, // Update textarea counter with remaining character left hideTextAreaCounters: ResizeUtil.hideTextArea });
  18. Sources • React Rally talk: https://www.youtube.com/ watch?v=mrtwImsEq5s • Moving from

    Require to Webpack 
 https://www.paypal-engineering.com/ 2015/08/07/1450/ • http://www.infoq.com/presentations/Simple- Made-Easy