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. Migrating safely to React (part 2) @jamischarles

  2. UI Engineer Send Money

  3. Back

  4. 2014

  5. C++ XML Markup

  6. Server {Dust.js} Client Kraken.js BB

  7. None
  8. • too many abstraction layers • low confidence • difficult

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

    • Complexity rises What happened?
  10. by Rich Hickey Clojure Simple Made Easy

  11. Speed Time Easy

  12. Speed Time Simple

  13. Speed Time Easy Simple

  14. “Simple → predictable → Reliable”

  15. None
  16. None
  17. 1 feature →

  18. None
  19. None
  20. None
  21. • React simplifies the view (fewer files) • Quick win

    • Push it to prod • Learn & evaluate Lessons learned
  22. Beyond the view

  23. 1. View layer 2. Routing layer 3. Data layer The

    pieces
  24. • Backbone Router + custom routing logic • complex •

    difficult to track url -> code • good opportunity Routing Layer
  25. /request Routing /send

  26. /request Routing /send

  27. /request Routing /send

  28. /request Routing /send

  29. /request Routing /send

  30. require('backboneSubroute'); module.exports = Backbone.SubRoute.extend({ routes: { '': 'showTransfer', ':type(/:action)': 'showTransfer'

    }, […]
  31. 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 } });
  32. // 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'));
  33. /request Routing /send

  34. 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;
  35. 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;
  36. 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;
  37. 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;
  38. /request Routing /send

  39. /request Routing /send

  40. 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;
  41. 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;
  42. • Url → code is easy now • Reduced complexity

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

    our state pain comes from here Data Layer
  44. Migrate 1 feature?

  45. None
  46. new feature

  47. None
  48. • Larger time investment • Greater risk • More testing

    / QA needed What about old data?
  49. None
  50. None
  51. None
  52. Optimize for future change

  53. – Michael Feathers “You have to build a barrier between

    yourself and the framework.”
  54. – Jamis Charles “Use as much vanilla JS as possible.”

  55. 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 },
  56. 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 },
  57. 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 });
  58. In Conclusion

  59. Solve real problems

  60. None
  61. Thank you

  62. 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
  63. @jamischarles #reactamsterdam

  64. None