Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

React on Rails - RailsConf 2017 (Phoenix)

228 views

Published on

Our journey from javascript spaghetti to React in a Rails App

Published in: Technology
  • Be the first to comment

  • Be the first to like this

React on Rails - RailsConf 2017 (Phoenix)

  1. 1. 🌵 React on Rails @jocranford
  2. 2. 🌵
  3. 3. 🌵 The Product and Market The Front End Development Scene The Evolution The Need for Change THE BEGINNING …
  4. 4. 🌵
  5. 5. 🌵
  6. 6. 🌵 Designed to change the way that you write JavaScript “Write less, do more.” Included by default in Rails 3.1 Why jQuery?
  7. 7. 🌵 Library of layout and grid components Easy to add via CDN or files Create a reasonably nice UI Smooth interactions Avoid spending hours playing with JS and CSS Why Bootstrap?
  8. 8. 🌵 YEARS PASS …
  9. 9. 🌵 MEANWHILE, THE WORLD MOVED ON … Browsers were standardising Mobile internet usage created new demands JavaScript was becoming a First Class Language Test frameworks became mainstream
  10. 10. 🌵
  11. 11. 🌵 The Emergence of React The Decision Evolution of the Asset Pipeline Building the First Components THE CHANGE!
  12. 12. 🌵 One way data flow Components represent the UI in a specific state JSX supported HTML and custom component tags Why is React Different?
  13. 13. 🌵 render() { return ( <aside className={this.getContainerStyle()} data-automation-id="Application_Navigation" > <div className={styles.top}> {this.renderCultureAmpLogo()} {this.renderEnvLabel()} <img src={Loader} className="loader" /> </div> <div className={styles.middle}> {this.renderNavigationLinks()} </div> <div className={styles.bottom}> {this.renderMasquradeUser()} {this.renderSuperUserAccount()} {this.renderUserAccount()} </div> </aside> ); }
  14. 14. 🌵 Why is React Different? One way data flow Components represent the UI in a specific state JSX supported HTML and custom component tags Re-renders when state changes Virtual DOM Backed by Facebook Easy to integrate without migrating everything
  15. 15. 🌵 Most of our users access our app using desktop browsers We can restrict support to modern browsers We hired a React export to help us through the transition SEO was not a concern Why React Suited Us One size DOESN’T fit all teams!
  16. 16. 🌵 Step 1: Embracing ES6 Block Scoping Immutable Constants Arrow Functions String Interpolation Spread Operator Classes For more: Luke Hoban’s es6features repo
  17. 17. 🌵
  18. 18. 🌵 Sprockets Default Rails Asset Pipeline Browser-ready assets Rails assets
  19. 19. 🌵 Sprockets webpack-bundled assets webpack React components Rails assets Browser-ready assets Adding Webpack - Step 1
  20. 20. 🌵 Sprockets webpack-bundled assets webpack React components Rails assets Browser-ready assets Adding Webpack - Step 1
  21. 21. 🌵 Sprockets Browser-ready assets Adding Webpack - Step 2 webpack React components webpack-bundled assets Rails assets
  22. 22. 🌵 Outputting a React Component in Haml def react_component(name, props = nil) content_tag( :div, nil, data: { react_component: name, react_props: props } ) end
  23. 23. 🌵 Registering and Rendering React Components function registerComponentType(typeName, ComponentType) { if (registeredComponentTypes.has(typeName)) { throw new Error(`component class "${typeName}" already registered`); } registeredComponentTypes.set(typeName, ComponentType); if (ready) mountComponents(typeName); } ... registerComponentType('NavigationBarApp', NavigationBarApp);
  24. 24. 🌵 function mountComponents() { $('[data-react-component]').toArray().map(node => { const typeName = $(node).data('react-component'); mountComponent(getRegisteredComponentType(typeName), node); }); } function mountComponent(ComponentType, node) { const props = Immutable.fromJS($(node).data('react-props')).toObject(); const element = <ComponentType {...props} />; ReactDOM.render(element, node); } $(() => { ready = true; mountComponents(); });
  25. 25. 🌵 function mountComponents() { $('[data-react-component]').toArray().map(node => { const typeName = $(node).data('react-component'); mountComponent(getRegisteredComponentType(typeName), node); }); } function mountComponent(ComponentType, node) { const props = Immutable.fromJS($(node).data('react-props')).toObject(); const element = <ComponentType {...props} />; ReactDOM.render(element, node); } $(() => { ready = true; mountComponents(); });
  26. 26. 🌵 Adding Jest Tests CSS Modules Losing Sprockets Introducing React Router and Redux Animations Immutable State THE EVOLUTION
  27. 27. 🌵 Created to work with React Originally mocked everything by default Difficult learning curve and bad developer experience Ultimately leads to cleaner code Jest now allows you to choose and by default does not mock by default Introducing Jest (and Mocking by Default)
  28. 28. 🌵 Testing Component Output with Shallow Rendering import TestUtils from 'react-addons-test-utils'; export default function shallowRender(element) { const shallowRenderer = TestUtils.createRenderer(); shallowRenderer.render(element); return shallowRenderer.getRenderOutput(); }
  29. 29. 🌵 Testing Component Output with Shallow Rendering it('should render the icon component', () => { const icon = shallowRender( <ShortcutLink label="label" icon="life-saver" popupMenuItems={items} /> ); const link = ShallowTestUtils.findWithType(icon, 'a'); ... });
  30. 30. 🌵 Testing Component Output with Snapshot Tests it('renders correctly when it is active', () => { const navigationLink = shallowRender( <NavigationLink linkText="text" path="path" isActive={true} iconType="icon" onClick={onClick} /> ); expect(navigationLink).toMatchSnapshot(); });
  31. 31. 🌵 Introducing CSS Modules
  32. 32. 🌵 .linkItem { color: red; font-size: 3em; text-align: center; &:hover { color: #fff; } } ... import styles from './NavigationBarApp.scss'; ... renderCultureAmpLogo() { return ( <a href="/"> <i className={styles.linkItem} /> </a> ); } SCSS File JavaScript File
  33. 33. 🌵 webpack Browser-ready assets Rails assets React components Getting rid of Sprockets
  34. 34. 🌵 Adding Single Page Transitions
  35. 35. 🌵 State Store Component Reducer Action with Payload Updated State
  36. 36. 🌵 JavaScript Routing Library New version matches components to URL sections Old version maintained indefinitely React Router
  37. 37. 🌵 User clicks Action: URL Changed Action: Check if data required Action: Ajax Request Promise Resolved Action: Data Updated Reducer processes data Components rerender
  38. 38. 🌵 npm Package Supports Springs Realistic animation curves Interruptible animations Issues: rendering PDFs and tests React Motion
  39. 39. 🌵 Immutable JS Immutable Objects Most useful: Map, List Methods return a new collection Protects against subtle bugs Highly optimised const { Map } = require('immutable') const map1 = Map({ a: 1, b: 2, c: 3 }) const map2 = map1.set('b', 50) map1.get('b') // 2 map2.get('b') // 50
  40. 40. 🌵 export const Score = StrongRecord( { favorable: null, unfavorable: null, neutral: null, significant: null, }, 'Score' ); ... SignificantScore.propTypes = { score: React.PropTypes.instanceOf(Score).isRequired, children: React.PropTypes.node, hero: React.PropTypes.bool, }; Using Defined Record Types
  41. 41. 🌵 New and better patterns for structuring code Functional programming principles How to write cleaner code that is easier to test What have we learned?
  42. 42. 🌵 Legacy JavaScript is still a thing Massive library of components Little thought for reusability so far Lack of overall consistency in CSS HAPPY EVER AFTER?
  43. 43. 🌵 EPILOGUE Moving towards a shared component library Experimenting with alternatives like Elm Starting a team to own the front end experience
  44. 44. 🌵 Thank You! @jocranford

×