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

React & Redux

React & Redux

A workshop for the team behind Neos

Tyll Weiß

March 07, 2016
Tweet

Other Decks in Technology

Transcript

  1. Let’s do this! Watcha got? • Take a look and

    learn the principles about React. • Learn whats behind `redux` and the unidirectional data flow. • Get some understanding on why and how we’ve chosen the stack for the UI rewrite. • Write an example ToDo Application.
  2. Essentials • ES6 & ES7 ◦ module syntax ◦ Arrow

    functions ◦ const, let ◦ Object destructuring ◦ Static property analyzers ◦ Classes ◦ Decorators • CQRS • Functional programming • Immutable data
  3. About React • A Library for building user interfaces. •

    React is NOT a framework. • It’s all about composable and modular components. • It makes no assumptions about your build/technology-stack.
  4. About React • A Library for building user interfaces. •

    React is NOT a framework. • It’s all about composable and modular components. • It makes no assumptions about your build/technology-stack. • Managing state with the DOM is hard. • Thats why React re-renders the whole application on each change.
  5. About the Virtual-DOM • Changes are first compared against the

    current virtual DOM element. • All changes are batched before applying them into the real DOM. • Optimized for performance, performance, performance! Every JavaScript developer in 2014
  6. The two pillars of React components Props: • Data should

    always have a clear unidirectional flow. In React this is implemented with `props`. • Props, or properties, are always passed from the outside into the components. • Props cannot be modified. • If a parent re-renders and passes a different value for the prop, the component will be re-rendered as well.
  7. The two pillars of React components Props: • Data should

    always have a clear unidirectional flow. In React this is implemented with `props`. • Props, or properties, are always passed from the outside into the components. • Props cannot be modified. • If a parent re-renders and passes a different value for the prop, the component will be re-rendered as well. State: • If you need to modify a value inside a componnt, you should first evaluate why. • If that’s not possible, a React component can hold it’s own state. • If you mutate the state, the component will be re-rendered.
  8. import React, {PropTypes} from 'react'; const Button = props =>

    ( <button role="button" onClick={props.onClick}> {props.children} </button> ); Button.propTypes = { onClick: PropTypes.func.isRequired, children: PropTypes.node.isRequired }; Button.defaultProps = { onClick: () => null } export default Button; React: Let’s build a component
  9. React: Let’s build a component import React, {PropTypes} from 'react';

    const Button = props => ( <button role="button" onClick={props.onClick}> {props.children} </button> ); Button.propTypes = { onClick: PropTypes.func.isRequired, children: PropTypes.node.isRequired }; Button.defaultProps = { onClick: () => null } export default Button; Fat-Arrow syntax, basically a function which has one argument `props` and returns the JSX markup.
  10. React: Let’s build a component import React, {PropTypes} from 'react';

    const Button = props => ( <button role="button" onClick={props.onClick}> {props.children} </button> ); Button.propTypes = { onClick: PropTypes.func.isRequired, children: PropTypes.node.isRequired }; Button.defaultProps = { onClick: () => null } export default Button; Fat-Arrow syntax, basically a function which has one argument `props` and returns the JSX markup. JSX is pretty much XHTML syntax which will be transpiled into raw JavaScript.
  11. React: Let’s build a component import React, {PropTypes} from 'react';

    const Button = props => ( <button role="button" onClick={props.onClick}> {props.children} </button> ); Button.propTypes = { onClick: PropTypes.func.isRequired, children: PropTypes.node.isRequired }; Button.defaultProps = { onClick: () => null } export default Button; Fat-Arrow syntax, basically a function which has one argument `props` and returns the JSX markup. JSX is pretty much XHTML syntax which will be transpiled into raw JavaScript. With React, you can implement a low-level type safety with `propTypes`.
  12. React: Let’s build a component optionalArray: React.PropTypes.array, optionalBool: React.PropTypes.bool, optionalFunc:

    React.PropTypes.func, optionalNumber: React.PropTypes.number, optionalObject: React.PropTypes.object, optionalString: React.PropTypes.string, optionalNode: React.PropTypes.node, optionalElement: React.PropTypes.element, optionalMessage: React.PropTypes.instanceOf(Message), optionalEnum: React.PropTypes.oneOf(['News', 'Photos']), optionalUnion: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.number, React.PropTypes.instanceOf(Message) ]) // Required. requiredFunc: React.PropTypes.func.isRequired, requiredAny: React.PropTypes.any.isRequired, customProp: function(props, propName, componentName) { if (!/matchme/.test(props[propName])) { return new Error('Validation failed!'); } } https://facebook.github.io/react/docs/reusable-components.html
  13. React: Let’s build a component import React, {PropTypes} from 'react';

    const Button = props => ( <button role="button" onClick={props.onClick}> {props.children} </button> ); Button.propTypes = { onClick: PropTypes.func.isRequired, children: PropTypes.node.isRequired }; Button.defaultProps = { onClick: () => null } export default Button; Fat-Arrow syntax, basically a function which has one argument `props` and returns the JSX markup. JSX is pretty much XHTML syntax which will be transpiled into raw JavaScript. With React, you can implement a low-level type safety with `propTypes`. … and you can provide default values, in case you want to make the life of a developer easier.
  14. React: Let’s build a component import React, {PropTypes} from 'react';

    const Button = props => ( <button role="button" onClick={props.onClick}> {props.children} </button> ); Button.propTypes = { onClick: PropTypes.func.isRequired, children: PropTypes.node.isRequired }; Button.defaultProps = { onClick: () => null } export default Button; What about state?
  15. React: Let’s build a stateful component import React, {Component, PropTypes}

    from 'react'; export default class Heading extends Component { constructor(props) { super(props); this.state = {label: 'Label loading...'}; } render() { return ( <h1> {this.state.label} </h1> ); } // ... }
  16. React: The component lifecycle First Render Re-render Removal Image from

    http://robert.katzki.de/posts/getting-started-with-react
  17. import React from 'react'; import ReactDOM from 'react-dom'; import {Button,

    Heading} from './Components/'; document.addEventListener('DOMContentLoaded', () => { ReactDOM.render( <div> <Heading labelEndpoint='/myJsonFile.txt' /> <Button> My fancy button </Button> </div>, document.getElementById('app') ); }); Rendering React components into the DOM
  18. What the Flux! • Data flow pattern made popular by

    facebook in 2014 for building react applications
  19. What the Flux! • Data flow pattern made popular by

    facebook in 2014 for building react applications Image from http://devangelist.de/facebook-flux-react/
  20. What the Flux! • Data flow pattern made popular by

    facebook in 2014 for building react applications • Basically reduced CQRS, but…
  21. What the Flux! • Data flow pattern made popular by

    facebook in 2014 for building react applications • Basically reduced CQRS, but… things got worse since people tended to create multiple instances of Flux in one application (UserStore, OrderStore).
  22. Say hello to Redux • “Redux evolves the ideas of

    Flux, but avoids its complexity by taking cues from Elm.” • Redux has only one state tree → Less things to instantiate and pass around.
  23. import { createStore } from 'redux'; function counter(state = 0,

    action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } } const actions = { increment: () => { type: 'INCREMENT' }, decrement: () => { type: 'DECREMENT' } } const store = createStore(counter); store.subscribe(() => console.log(store.getState()) ); store.dispatch(actions.increment()) // 1 store.dispatch(actions.increment()) // 2 store.dispatch(actions.decrement()) // 1 Redux: Let’s see some code
  24. import { createStore } from 'redux'; function counter(state = 0,

    action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } } const actions = { increment: () => { type: 'INCREMENT' }, decrement: () => { type: 'DECREMENT' } } const store = createStore(counter); store.subscribe(() => console.log(store.getState()) ); store.dispatch(actions.increment()) // 1 store.dispatch(actions.increment()) // 2 store.dispatch(actions.decrement()) // 1 Redux: Let’s see some code 1) Reducer, mutates and returns the new state.
  25. import { createStore } from 'redux'; function counter(state = 0,

    action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } } const actions = { increment: () => { type: 'INCREMENT' }, decrement: () => { type: 'DECREMENT' } } const store = createStore(counter); store.subscribe(() => console.log(store.getState()) ); store.dispatch(actions.increment()) // 1 store.dispatch(actions.increment()) // 2 store.dispatch(actions.decrement()) // 1 Redux: Let’s see some code 1) Reducer, mutates and returns the new state. 2) Action creators will create the action objects with the desired payload
  26. import { createStore } from 'redux'; function counter(state = 0,

    action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } } const actions = { increment: () => { type: 'INCREMENT' }, decrement: () => { type: 'DECREMENT' } } const store = createStore(counter); store.subscribe(() => console.log(store.getState()) ); store.dispatch(actions.increment()) // 1 store.dispatch(actions.increment()) // 2 store.dispatch(actions.decrement()) // 1 Redux: Let’s see some code 1) Reducer, mutates and returns the new state. 2) Create the store instance. 2) Action creators will create the action objects with the desired payload
  27. import { createStore } from 'redux'; function counter(state = 0,

    action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } } const actions = { increment: () => { type: 'INCREMENT' }, decrement: () => { type: 'DECREMENT' } } const store = createStore(counter); store.subscribe(() => console.log(store.getState()) ); store.dispatch(actions.increment()) // 1 store.dispatch(actions.increment()) // 2 store.dispatch(actions.decrement()) // 1 Redux: Let’s see some code 1) Reducer, mutates and returns the new state. 2) Create the store instance. 3) Subscribe to updates. 2) Action creators will create the action objects with the desired payload
  28. import { createStore } from 'redux'; function counter(state = 0,

    action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } } const actions = { increment: () => { type: 'INCREMENT' }, decrement: () => { type: 'DECREMENT' } } const store = createStore(counter); store.subscribe(() => console.log(store.getState()) ); store.dispatch(actions.increment()) // 1 store.dispatch(actions.increment()) // 2 store.dispatch(actions.decrement()) // 1 Redux: Let’s see some code 1) Reducer, mutates and returns the new state. 2) Create the store instance. 3) Subscribe to updates. 4) Dispatch actions, which will mutate the state through the switch statement. 2) Action creators will create the action objects with the desired payload
  29. import React, {Component, PropTypes} from 'react'; import {connect} from 'react-redux';

    import {actions} from './redux/'; @connect(state => ({ isVisible: state.siteHeader.isVisible }), { toggleSiteHeaderVisibility: actions.toggleSiteHeaderVisibility }) class SiteHeaderToggler extends Component { static propTypes = { isVisible: PropTypes.bool.isRequired, toggleSiteHeaderVisibility: PropTypes.func.isRequired }; render() { const {isVisible, toggleSiteHeaderVisibility} = this.props; return <a className={isVisible ? 'yep' : 'nope'} onClick={() => toggleSiteHeaderVisibility()}>Click me</a>; } } Redux: Connecting Redux with React
  30. - components - Button - index.js - index.spec.js - style.css

    - index.js - containers - AppBar - LeftSideBar - ContentView - index.js - redux - myActionAndReducer.js - index.js - index.js Structure of an organized React project
  31. import React from 'react'; import ReactDOM from 'react-dom'; import {Provider}

    from 'react-redux'; import AppContainer from './containers/'; import {configureStore} from './redux/'; document.addEventListener('DOMContentLoaded', () => { const serverState = {/* ... */}; const store = configureStore(serverState); ReactDOM.render( <div> <Provider store={store}> <AppContainer /> </Provider> </div>, document.getElementById('app') ); }); Rendering the application into the DOM
  32. Build-System • Webpack • Babel • PostCSS • ESLint •

    StyleLint • Karma, Chai, Sinon, Enzyme, WebdriverIO • NPM • Travis CI & Codeclimate
  33. Build-System • Webpack • Babel • PostCSS • ESLint •

    StyleLint • Karma, Chai, Sinon, Enzyme, WebdriverIO • NPM • Travis CI & Codeclimate More info on https://github.com/PackageFactory/PackageFactory.Guevara#development-commands
  34. Application-Stack • react • redux • react-redux • redux-actions •

    redux-saga • immutable.js • virtual-dom • lodash utilities • etc.
  35. Application-Structure Guest Neos server instance Redux Store Host and guest

    frame are independent & encapsulated applications Host
  36. Application-Structure Guest Neos server instance Redux Store Host and guest

    frame are independent & encapsulated applications The redux store handles all interactions & business logic Host
  37. Application-Structure Host Guest Neos server instance Redux Store Host and

    guest frame are independent & encapsulated applications The redux store handles all interactions & business logic Neos JS API Handles the communication with the server.