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

Demystifying React, Redux, JSX and Webpack

Demystifying React, Redux, JSX and Webpack

The world of Javascript is vast and exciting... but it can also be quite scary! It is an innovative world in which new technologies appear all the time, which can make it difficult for developers to keep up to date.

Some of the most famous of these technologies are probably React and Redux. They changed the way we conceive User Interfaces, both in the browser and in mobile applications. But understanding them can be challenging: let's demystify them together so that you can enter the world of Javascript with confidence!

364d59ac0b4b4e5eee8aeb27a127d176?s=128

Titouan Galopin

March 28, 2019
Tweet

Transcript

  1. React and Redux with Symfony and Webpack Encore Titouan GALOPIN

    SymfonyLive Paris 2019
  2. 2 Titouan Galopin Product Manager SymfonyInsight insight.symfony.com Certified Software engineer

  3. Agenda 1. What’s React? 2. Components 3. Webpack Encore 4.

    Redux 5. Example 3
  4. 4 1. What’s React?

  5. 5 How should we structure Javascript applications?

  6. 6 AngularJS Vue Backbone.js Dart Ember.js Dojo Meteor ...

  7. 7 There is a consensus here: MVC, or MVVM, or

    MVW, ...
  8. 8 Models

  9. 9 Models = Observable objects

  10. 10 Bidirectional data-binding An change in the model updates the

    view An change in the view updates the model
  11. 11 When a change occurs somewhere, it mutates the other

    part of the system
  12. 12 But mutations are complex

  13. 13 Complex to apply Complex to reproduce Complex to debug

  14. 14 React aims at simplifying this

  15. 15 React does not use mutations

  16. 16 Model Observable model Two-way data flow View

  17. 17 Model Observable model Two-way data flow View React One-way

    data flow Store Dispatcher View
  18. View 18 Model Observable model Two-way data flow View React

    One-way data flow Store Dispatcher View
  19. 19 Model Observable model Two-way data flow View React One-way

    data flow Store Dispatcher View
  20. 20 React re-renders all your view when data changes

  21. 21 But isn’t that super slow? What about user inputs?

  22. 22 We want the benefits to blow the view away

    and re-render it ... … while dealing properly with every edge cases
  23. 23 That’s React

  24. 24 React is a Javascript library for building User Interfaces

  25. 25 2. Components

  26. 26 React’s main concept is Components

  27. 27 A component = A description of a part of

    your UI that depends on your current model
  28. 28 It’s more than a template though

  29. 29 Store Dispatcher View Each component has its own state

    and behavior This is component
  30. 30 The aim? Abstract away the implementation details of parts

    of the UI
  31. 31

  32. 32 <Autocomplete source="/api/users" onSelect={this.handleUserSelected} />

  33. 33 <Autocomplete source="/api/users" onSelect={this.handleUserSelected} /> Property

  34. 34 But how does it work?

  35. 35 A component = A state + A way to

    display that state
  36. 36 Store Dispatcher View React handles the Dispatcher and the

    Store You only implement the view and define the structure of the Store
  37. 37 The state is a single Javascript object

  38. 38 The view is described by a single render() function

  39. 39 Store (state) Initial state

  40. 40 render Store (state) View (components) Initial state

  41. 41

  42. 42 class Autocomplete extends Component { constructor() { this.state =

    { results: [] }; } }
  43. 43 class Autocomplete extends Component { // ... render() {

    return ( <ul> {this.state.results.map(function(result) { return ( <li>{result.name}</li> ); })} </ul> ); } }
  44. 44 class Autocomplete extends Component { // ... render() {

    return ( <ul> {this.state.results.map(function(result) { return ( <li>{result.name}</li> ); })} </ul> ); } } Use of the state
  45. 45 class Autocomplete extends Component { // ... render() {

    return ( <ul> {this.state.results.map(function(result) { return ( <li>{result.name}</li> ); })} </ul> ); } } Use of the state To use a property: this.props.source
  46. State = Local data that will change over time Props

    = Non-local data, read-only for the component 46
  47. If either the state or the properties change, the component

    will be re-rendered 47
  48. 48 class Autocomplete extends Component { // ... render() {

    return ( <ul> {this.state.results.map(function(result) { return ( <li>{result.name}</li> ); })} </ul> ); } }
  49. 49 class Autocomplete extends Component { // ... render() {

    return ( <ul> {this.state.results.map(function(result) { return ( <li>{result.name}</li> ); })} </ul> ); } } What’s that?!
  50. 50 JSX is a formatting language used by React to

    express a representation of views
  51. 51 It is compiled to Javascript <li className="hello"> becomes React.DOM.li({

    className: 'hello'})
  52. 52 class Autocomplete extends Component { // ... render() {

    return ( <ul> {this.state.results.map(function(result) { return ( <li>{result.name}</li> ); })} </ul> ); } } Not real HTML tags but components
  53. 53 render() returns a tree of components

  54. 54 Now, how to update the view?

  55. 55 Instead of updates (mutations) React uses reconciliation

  56. 56 React compares the view representations and apply the changes

    to the DOM
  57. 57 setState()

  58. 58 class Autocomplete extends Component { // ... handleChange(event) {

    api.fetchResults(event.target.value).then(results => { this.setState({ results: results }) }); } // ... render() { return ( <div> <input type="text" onChange={this.handleChange} /> // ... </div> ); } }
  59. 59 setState() merges the current state and the given data

    ...
  60. 60 … then calls recursively render() on the component and

    all its children
  61. 61 render Store (state) View (components) Initial state

  62. 62 Dispatcher (React) setState render Store (state) View (components) Initial

    state
  63. 63 Dispatcher (React) setState render Handled by React Store (state)

    View (components) Initial state
  64. 64 Now you know React!

  65. 65 3. Webpack Encore

  66. 66 Webpack is a build tool It lets you manipulate

    your Javascript and CSS before using it in production (JSX, minification, …)
  67. 67 Webpack Encore wraps Webpack around a nice API to

    improve its Developer Experience
  68. 68 Webpack Encore is awesome to compile React apps to

    normal Javascript
  69. 69 composer req --dev webpack yarn install https://symfony.com/doc/current/frontend.html

  70. 70 yarn add @babel/preset-react react react-dom prop-types

  71. 71 // webpack.config.js const Encore = require( '@symfony/webpack-encore' ); Encore

    // ... .enableReactPreset(); module.exports = Encore.getWebpackConfig();
  72. 72 // app.js import ReactDOM from 'react-dom'; import {App} from

    './App.js'; ReactDOM.render( <App />, document.getElementById('portfolio') );
  73. 73 // app.js import ReactDOM from 'react-dom'; import {App} from

    './App.js'; ReactDOM.render( <App />, document.getElementById('portfolio') ); Tree of components
  74. 74 4. Redux

  75. 75 One component = One state object

  76. 76 In a real React application, you will soon have

    dozens of React components
  77. 77 That’s a lot of states

  78. 78 How to synchronize them ?

  79. 79 Redux

  80. 80 Redux helps you manage a single State for your

    whole application and use it in your components
  81. 81 Redux works using Actions and Reducers

  82. 82 Your components receive data from global state as props

  83. 83 Your components dispatch actions with custom data

  84. 84 Reducers transform these actions into changes to the global

    state
  85. 85 Global state Initial global state Redux

  86. 86 Global state Initial global state Props View Props View

    Redux Component Component mapStateToProps
  87. 87 Global state Initial global state Dispatcher Props View Props

    View Redux Component Component mapStateToProps dispatch
  88. 88 Global state Initial global state Dispatcher Props View Props

    View Redux Component Component mapStateToProps dispatch Reducers
  89. 89 5. Example

  90. 90 SymfonyInsight Portfolio

  91. 91

  92. 92 Global state { dateInterval: string, isLoading: boolean, projects: Project[]

    | null, lastUpdated: Date | null, }
  93. 93

  94. 94

  95. 95

  96. 96 function mapStateToProps(globalState) { return { dateInterval: state.dateInterval, projects: state.projects,

    lastUpdated: state.lastUpdated, }; } class SidebarView extends Component { // ... } export const Sidebar = connect(mapStateToProps)(SidebarView);
  97. 97 function mapStateToProps(globalState) { return { dateInterval: state.dateInterval, projects: state.projects,

    lastUpdated: state.lastUpdated, }; } class SidebarView extends Component { // ... } export const Sidebar = connect(mapStateToProps)(SidebarView); Redux
  98. class SidebarView extends Component { // ... render() { return

    ( <div className="Sidebar"> <DateIntervalChooser currentValue={this.props.dateInterval} onChange={(i) => this.updateDateInterval(i)} /> <ul className="Sidebar-menu"> <li><Link to="/portfolio">All projects</Link></li> {this.props.projects.map( project => <SidebarItem project={project} /> )} </ul> <div className="Sidebar-last-updated"> Data last updated {moment(this.props.lastUpdated).fromNow()} </div> </div> ); 98
  99. 99 class SidebarView extends Component { updateDateInterval(interval) { this.props.dispatch({ type:

    'SET_DATE_INTERVAL', dateInterval: interval }); } // ... }
  100. 100 class SidebarView extends Component { updateDateInterval(interval) { this.props.dispatch({ type:

    'SET_DATE_INTERVAL', dateInterval: interval }); } // ... } Redux
  101. 101 import {storage} from '../storage/storage'; export const dateIntervalReducer = (state,

    action) => { if (typeof state === 'undefined') { return storage.get('portfolio-date-interval', 2); } switch (action.type) { case 'SET_DATE_INTERVAL': storage.set('portfolio-date-interval', action.dateInterval); return action.dateInterval; default: return state; } };
  102. 102 import {storage} from '../storage/storage'; export const dateIntervalReducer = (state,

    action) => { if (typeof state === 'undefined') { return storage.get('portfolio-date-interval', 2); } switch (action.type) { case 'SET_DATE_INTERVAL': storage.set('portfolio-date-interval', action.dateInterval); return action.dateInterval; default: return state; } }; Current state
  103. 103 import {storage} from '../storage/storage'; export const dateIntervalReducer = (state,

    action) => { if (typeof state === 'undefined') { return storage.get('portfolio-date-interval', 2); } switch (action.type) { case 'SET_DATE_INTERVAL': storage.set('portfolio-date-interval', action.dateInterval); return action.dateInterval; default: return state; } }; Current state Initial state
  104. 104 import {storage} from '../storage/storage'; export const dateIntervalReducer = (state,

    action) => { if (typeof state === 'undefined') { return storage.get('portfolio-date-interval', 2); } switch (action.type) { case 'SET_DATE_INTERVAL': storage.set('portfolio-date-interval', action.dateInterval); return action.dateInterval; default: return state; } }; Current state Initial state New state
  105. 105 import {combineReducers} from 'redux'; export const rootReducer = combineReducers({

    dateInterval: dateIntervalReducer, isLoading: isLoadingReducer, projects: projectsReducer, lastUpdated: lastUpdatedReducer, // ... });
  106. 106 Conclusion

  107. Thanks! 107 For any question: ▪ @titouangalopin on Twitter ▪

    titouan.galopin @symfony.com