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

A portal to the future

A portal to the future

Fiber release brought with it some pretty cool features but the best is yet to come!

First, let’s talk about array children, error boundaries and renderToStream.

Then, I’ll give you a sneak peak into one of the upcoming features that might change the way you build your React applications. Portals open up a lot of opportunities and traps!

Let’s explore 2 of them: reducing client bundle sizes and easier state management.

Siddharth Kshetrapal

September 14, 2017
Tweet

More Decks by Siddharth Kshetrapal

Other Decks in Technology

Transcript

  1. A portal to the future!

    View full-size slide

  2. Siddharth Kshetrapal

    View full-size slide

  3. @siddharthkp

    View full-size slide

  4. @siddharthkp
    javascript architect

    View full-size slide

  5. @siddharthkp
    design, javascript, web performance

    View full-size slide

  6. bit.ly/siddharthkpshow

    View full-size slide

  7. i teach react
    bit.ly/react-training

    View full-size slide

  8. co-organise react bangalore

    View full-size slide

  9. i have stickers
    grab them right outside

    View full-size slide

  10. A portal to the future!

    View full-size slide

  11. is Fiber ready yet?

    View full-size slide

  12. http://isfiberreadyyet.com

    View full-size slide

  13. what is fiber?

    View full-size slide

  14. ground-up rewrite
    of the diffing algorithm
    React 16

    View full-size slide

  15. The diffing algorithm React uses to tell
    which parts need to be changed

    View full-size slide

  16. what does this mean for us?
    React 16

    View full-size slide

  17. what’s
    new?
    why
    rewrite?
    ooh
    portals!

    View full-size slide

  18. 4 tiny complaints

    View full-size slide

  19. Hello!
    I'm Siddharth

    View full-size slide

  20. const MyComponent = () => {
    return (
    Hello!
    I'm Siddharth
    )
    }

    View full-size slide

  21. const MyComponent = () => {
    return (
    Hello!
    I'm Siddharth
    )
    }

    View full-size slide

  22. const MyComponent = () => {
    return (

    Hello!
    I'm Siddharth

    )
    }

    View full-size slide

  23. const MyComponent = () => {
    return (
    Item 1
    Item 2
    )
    }

    View full-size slide

  24. const MyComponent = () => {
    return (

    Item 1
    Item 2

    )
    }

    View full-size slide

  25. const MyComponent = () => {
    return (

    Item 1
    Item 2

    )
    }

    View full-size slide

  26. const MyComponent = () => {
    return (
    [
    Item 1,
    Item 2
    ]
    )
    }

    View full-size slide

  27. componentDidCatch(error, info)

    View full-size slide

  28. error boundaries
    try/catch, but for components

    View full-size slide

  29. import React from 'react'
    const MyComponent = () => {
    return (



    )
    }

    View full-size slide

  30. import React from 'react'
    const MyComponent = () => {
    return (



    )
    }

    View full-size slide

  31. class ErrorBoundary extends React.Component {
    constructor(props) {
    super(props)
    this.state = { hasError: false }
    }
    componentDidCatch(error, info) {
    this.setState({ hasError: true });
    }
    render() {
    if (this.state.hasError) return Something broke
    else return {this.props.children}
    }
    }

    View full-size slide

  32. class ErrorBoundary extends React.Component {
    constructor(props) {
    super(props)
    this.state = { hasError: false }
    }
    componentDidCatch(error, info) {
    this.setState({ hasError: true });
    }
    render() {
    if (this.state.hasError) return Something broke
    else return {this.props.children}
    }
    }

    View full-size slide

  33. class ErrorBoundary extends React.Component {
    constructor(props) {
    super(props)
    this.state = { hasError: false }
    }
    componentDidCatch(error, info) {
    this.setState({ hasError: true });
    }
    render() {
    if (this.state.hasError) return Something broke
    else return {this.props.children}
    }
    }

    View full-size slide

  34. class ErrorBoundary extends React.Component {
    constructor(props) {
    super(props)
    this.state = { hasError: false }
    }
    componentDidCatch(error, info) {
    this.setState({ hasError: true });
    }
    render() {
    if (this.state.hasError) return Something broke
    else return {this.props.children}
    }
    }

    View full-size slide

  35. ReactDOM.renderToString
    server side rendering

    View full-size slide

  36. on Node 6 82ms
    github.com/aickin/react-16-ssr-perf

    View full-size slide

  37. on Node 6 82ms
    28ms
    github.com/aickin/react-16-ssr-perf

    View full-size slide

  38. on Node 6 82ms
    28ms
    github.com/aickin/react-16-ssr-perf
    65% faster

    View full-size slide

  39. import { renderToString } from 'react-dom/server'
    server.get('/', (req, res) => {
    const html = renderToString()
    res.end(html)
    })

    View full-size slide

  40. import { renderToString } from 'react-dom/server'
    server.get('/', (req, res) => {
    const html = renderToString()
    res.end(html)
    })

    View full-size slide

  41. import { renderToString } from 'react-dom/server'
    server.get('/', (req, res) => {
    const html = renderToString()
    res.end(html)
    })

    View full-size slide

  42. import { renderToString } from 'react-dom/server'
    server.get('/', (req, res) => {
    const html = renderToString()
    res.end(html)
    })

    View full-size slide

  43. ReactDOM.renderToNodeStream

    View full-size slide

  44. import { renderToNodeStream } from 'react-dom/server'
    server.get('/', (req, res) => {
    renderToNodeStream()
    .pipe(res)
    .on('end', () => res.end())
    })

    View full-size slide

  45. import { renderToNodeStream } from 'react-dom/server'
    server.get('/', (req, res) => {
    renderToNodeStream()
    .pipe(res)
    .on('end', () => res.end())
    })

    View full-size slide

  46. import { renderToNodeStream } from 'react-dom/server'
    server.get('/', (req, res) => {
    renderToNodeStream()
    .pipe(res)
    .on('end', () => res.end())
    })

    View full-size slide

  47. import { renderToNodeStream } from 'react-dom/server'
    server.get('/', (req, res) => {
    renderToNodeStream()
    .pipe(res)
    .on('end', () => res.end())
    })

    View full-size slide

  48. $ npm install react@next react-dom@next

    View full-size slide

  49. better scheduling of work

    View full-size slide

  50. Filter users

    View full-size slide

  51. Filter users

    View full-size slide

  52. Filter users

    View full-size slide

  53. Filter users
    HUMAN
    JAVASCRIPT
    DOM

    View full-size slide

  54. s
    Filter users

    View full-size slide

  55. s
    Filter users
    keydown event 's'

    View full-size slide

  56. s
    s
    keydown event 's'
    repaint

    View full-size slide

  57. s i d d h a r t h
    siddharth

    View full-size slide

  58. Filter users

    View full-size slide

  59. s
    s
    render filtered list

    View full-size slide

  60. s i d d h a r t h
    s

    View full-size slide

  61. s i d d h a r t h
    siddh

    View full-size slide

  62. s i d d h a r t h
    siddharth

    View full-size slide

  63. s i d d h a r t h
    siddharth

    View full-size slide

  64. s i d d h a r t h
    siddharth

    View full-size slide

  65. s i d d h a r t h
    siddharth

    View full-size slide

  66. s i d d h a r t h
    siddharth

    View full-size slide

  67. split work at
    component boundaries

    View full-size slide

  68. cooperative multitasking

    View full-size slide

  69. /* node_modules/react-dom/cjs/react-dom.development.js /*
    var ReactDOMFeatureFlags = {
    fiberAsyncScheduling: false,
    useFiber: true,
    }

    View full-size slide

  70. /* node_modules/react-dom/cjs/react-dom.development.js /*
    var ReactDOMFeatureFlags = {
    fiberAsyncScheduling: false,
    useFiber: true,
    }

    View full-size slide

  71. /* node_modules/react-dom/cjs/react-dom.development.js /*
    var ReactDOMFeatureFlags = {
    fiberAsyncScheduling: true,
    useFiber: true,
    }

    View full-size slide

  72. /* your code /*
    ReactDOM.unstable_deferredUpdates(() => {
    this.setState({ filteredUsers: filteredUsers })
    })

    View full-size slide

  73. /* your code /*
    ReactDOM.unstable_deferredUpdates(() => {
    this.setState({ filteredUsers: filteredUsers })
    })

    View full-size slide

  74. /* your code /*
    ReactDOM.unstable_deferredUpdates(() => {
    this.setState({ filteredUsers: filteredUsers })
    })
    /* this is a low priority task /*

    View full-size slide

  75. ReactDOM.render(, target)

    View full-size slide

  76. ReactDOM.createPortal(, target)

    View full-size slide

  77. real world problem #1

    View full-size slide

  78. const html = ReactDOM.renderToString()

    View full-size slide

  79. const html = ReactDOM.renderToString()
    res.send(`
    ${html}
    `)

    View full-size slide

  80. const html = ReactDOM.renderToString()
    res.send(`
    ${html}
    `)

    View full-size slide

  81. const html = ReactDOM.renderToString()
    res.send(`
    ${html}
    `)
    ReactDOM.hydrate(
    ,
    document.getElementById('root')
    )

    View full-size slide

  82. tomato
    dynamic
    dynamic

    View full-size slide

  83. const html = ReactDOM.renderToString()
    res.send(`
    ${html}
    `)
    ReactDOM.hydrate(
    ,
    document.getElementById('root')
    )

    View full-size slide

  84. const html = ReactDOM.renderToString()
    res.send(`
    ${html}
    `)
    ReactDOM.hydrate(
    ,
    document.getElementById('root')
    )

    View full-size slide

  85. const html = ReactDOM.renderToString()
    res.send(`
    ${html}
    `)
    ReactDOM.hydrate(
    ,
    document.getElementById('root')
    )

    View full-size slide

  86. const html = ReactDOM.renderToString()
    res.send(`
    ${html}

    `)
    ReactDOM.hydrate(
    ,
    document.getElementById('root')
    )

    View full-size slide

  87. const html = ReactDOM.renderToString()
    res.send(`
    ${html}

    `)
    ReactDOM.hydrate(
    ,
    document.getElementById('client-root')
    )

    View full-size slide

  88. const html = ReactDOM.renderToString()
    res.send(`
    ${html}

    `)
    ReactDOM.hydrate(
    ,
    document.getElementById('client-root')
    )

    View full-size slide

  89. class Client extends React.Component {
    }

    View full-size slide

  90. class Client extends React.Component {
    constructor() {
    super()
    }
    }

    View full-size slide

  91. class Client extends React.Component {
    constructor() {
    super()
    document.getElementById('cart').innerHTML = ''
    }
    }

    View full-size slide

  92. class Client extends React.Component {
    constructor() {
    super()
    document.getElementById('cart').innerHTML = ''
    }
    render() {
    }
    }

    View full-size slide

  93. class Client extends React.Component {
    constructor() {
    super()
    document.getElementById('cart').innerHTML = ''
    }
    render() {
    return

    }
    }

    View full-size slide

  94. class Client extends React.Component {
    constructor() {
    super()
    document.getElementById('cart').innerHTML = ''
    }
    render() {
    const cartEl = document.getElementById('cart')
    return

    }
    }

    View full-size slide

  95. class Client extends React.Component {
    constructor() {
    super()
    document.getElementById('cart').innerHTML = ''
    }
    render() {
    const cartEl = document.getElementById('cart')
    return
    { ReactDOM.unstable_createPortal(, cartEl) }

    }
    }

    View full-size slide

  96. class Client extends React.Component {
    constructor() {
    super()
    document.getElementById('cart').innerHTML = ''
    }
    render() {
    const cartEl = document.getElementById('cart')
    return
    { ReactDOM.unstable_createPortal(, cartEl) }

    }
    }

    View full-size slide

  97. class Client extends React.Component {
    constructor() {
    super()
    document.getElementById('cart').innerHTML = ''
    document.getElementById('menu').innerHTML = ''
    }
    render() {
    const cartEl = document.getElementById('cart')
    return
    { ReactDOM.unstable_createPortal(, cartEl) }

    }
    }

    View full-size slide

  98. class Client extends React.Component {
    constructor() {
    super()
    document.getElementById('cart').innerHTML = ''
    document.getElementById('menu').innerHTML = ''
    }
    render() {
    const cartEl = document.getElementById('cart')
    return
    { ReactDOM.unstable_createPortal(, cartEl) }

    }
    }

    View full-size slide

  99. class Client extends React.Component {
    constructor() {
    super()
    document.getElementById('cart').innerHTML = ''
    document.getElementById('menu').innerHTML = ''
    }
    render() {
    const cartEl = document.getElementById('cart')
    const menuEl = document.getElementById('menu')
    return
    { ReactDOM.unstable_createPortal(, cartEl) }

    }

    View full-size slide

  100. class Client extends React.Component {
    constructor() {
    super()
    document.getElementById('cart').innerHTML = ''
    document.getElementById('menu').innerHTML = ''
    }
    render() {
    const cartEl = document.getElementById('cart')
    const menuEl = document.getElementById('menu')
    return
    { ReactDOM.unstable_createPortal(, catdEl) }
    { ReactDOM.unstable_createPortal(, menuEl) }

    View full-size slide

  101. yay, performance!

    View full-size slide

  102. real world problem #2

    View full-size slide

  103. tomato
    state: { items: [
    { id: 1, count: 0 },
    { id: 2, count: 0 },
    ...
    ]}
    Menu.js

    View full-size slide

  104. tomato
    Menu.js state: { items: [
    { id: 1, count: 0 },
    { id: 2, count: 0 },
    ...
    ]}

    View full-size slide

  105. tomato
    Menu.js state: { items: [
    { id: 1, count: 0 },
    { id: 2, count: 1 },
    ...
    ]}
    onClick = (item) => {
    this.setState(...)
    }

    View full-size slide

  106. tomato
    Menu.js state: { items: [
    { id: 1, count: 1 },
    { id: 2, count: 1 },
    ...
    ]}
    onClick = (item) => {
    this.setState(...)
    }

    View full-size slide

  107. tomato
    Menu.js state: { items: [
    { id: 1, count: 2 },
    { id: 2, count: 1 },
    ...
    ]}
    onClick = (item) => {
    this.setState(...)
    }

    View full-size slide

  108. tomato
    Menu.js
    Sidebar.js
    state: { items: [
    { id: 1, count: 0 },
    { id: 2, count: 0 },
    ...
    ]}
    onClick = (item) => {
    this.setState(...)
    }

    View full-size slide

  109. tomato
    Menu.js
    Sidebar.js
    state: { items: [
    { id: 1, count: 0 },
    { id: 2, count: 1 },
    ...
    ]}
    onClick = (item) => {
    this.setState(...)
    }

    View full-size slide

  110. tomato
    Menu.js
    Sidebar.js
    state: { items: [
    { id: 1, count: 0 },
    { id: 2, count: 1 },
    ...
    ]}
    onClick = (item) => {
    this.setState(...)
    }

    View full-size slide

  111. tomato
    Menu.js
    Sidebar.js
    state: { items: [
    { id: 1, count: 0 },
    { id: 2, count: 0 },
    ...
    ]}
    onClick = (item) => {
    this.setState(...)
    }

    View full-size slide

  112. tomato
    state: { items: [ { id: 1, count: 0 }, { id: 2, count: 0 }, ...]}
    o
    Sidebar.js
    App.js
    Menu.js

    View full-size slide

  113. tomato
    state: { items: [ { id: 1, count: 0 }, { id: 2, count: 0 }, ...]}
    o
    Sidebar.js
    App.js
    Menu.js
    props

    View full-size slide

  114. tomato
    state: { items: [ { id: 1, count: 0 }, { id: 2, count: 0 }, ...]}
    o
    Sidebar.js
    App.js
    Menu.js

    View full-size slide

  115. tomato
    state: { items: [ { id: 1, count: 0 }, { id: 2, count: 0 }, ...]}
    o
    Sidebar.js
    Menu.js
    onClick = (item) => this.setState(...)
    App.js

    View full-size slide

  116. tomato
    state: { items: [ { id: 1, count: 0 }, { id: 2, count: 0 }, ...]}
    o
    Sidebar.js
    Menu.js
    onClick = (item) => this.setState(...)
    App.js
    function

    View full-size slide

  117. tomato
    state: { items: [ { id: 1, count: 0 }, { id: 2, count: 0 }, ...]}
    o
    Sidebar.js
    Menu.js
    onClick = (item) => this.setState(...)
    App.js

    View full-size slide

  118. tomato
    state: { items: [ { id: 1, count: 0 }, { id: 2, count: 0 }, ...]}
    o
    Sidebar.js
    Menu.js
    onClick = (item) => this.setState(...)
    App.js
    function call

    View full-size slide

  119. tomato
    state: { items: [ { id: 1, count: 0 }, { id: 2, count: 1 }, ...]}
    o
    Sidebar.js
    onClick = (item) => this.setState(...)
    App.js
    Menu.js

    View full-size slide

  120. tomato
    Sidebar.js
    state: { items: [ { id: 1, count: 0 }, { id: 2, count: 1 }, ...]}
    o
    Sidebar.js
    onClick = (item) => this.setState(...)
    App.js
    Menu.js
    props

    View full-size slide

  121. tomato
    Sidebar.js
    state: { items: [ { id: 1, count: 0 }, { id: 2, count: 1 }, ...]}
    o
    Sidebar.js
    onClick = (item) => this.setState(...)
    App.js
    Menu.js
    props

    View full-size slide

  122. tomato
    { items: [ { id: 1, count: 0 }, { id: 2, count: 0 }, ...] }
    Store
    Sidebar.js
    Menu.js

    View full-size slide

  123. tomato
    { items: [ { id: 1, count: 0 }, { id: 2, count: 0 }, ...] }
    Store
    Sidebar.js
    Menu.js

    View full-size slide

  124. tomato
    { items: [ { id: 1, count: 0 }, { id: 2, count: 0 }, ...] }
    Store
    Sidebar.js
    Menu.js

    View full-size slide

  125. tomato
    { items: [ { id: 1, count: 0 }, { id: 2, count: 0 }, ...] }
    Store
    Sidebar.js
    Menu.js
    dispatch('ADD_ITEM', {id: 1})

    View full-size slide

  126. tomato
    { items: [ { id: 1, count: 0 }, { id: 2, count: 1 }, ...] }
    Store
    Sidebar.js
    Menu.js

    View full-size slide

  127. tomato
    { items: [ { id: 1, count: 0 }, { id: 2, count: 1 }, ...] }
    Store
    Sidebar.js
    Menu.js

    View full-size slide

  128. tomato
    Menu.js
    Sidebar.js
    state: { items: [
    { id: 1, count: 0 },
    { id: 2, count: 0 },
    ...
    ]}
    onClick = (item) => {
    this.setState(...)
    }

    View full-size slide

  129. tomato
    Menu.js
    Sidebar.js
    state: { items: [
    { id: 1, count: 0 },
    { id: 2, count: 1 },
    ...
    ]}
    onClick = (item) => {
    this.setState(...)
    }

    View full-size slide

  130. tomato
    Menu.js
    Sidebar.js
    state: { items: [
    { id: 1, count: 0 },
    { id: 2, count: 1 },
    ...
    ]}
    onClick = (item) => {
    this.setState(...)
    }
    render() {
    // create portal
    }

    View full-size slide

  131. tomato
    Menu.js
    Sidebar.js
    state: { items: [
    { id: 1, count: 0 },
    { id: 2, count: 1 },
    ...
    ]}
    onClick = (item) => {
    this.setState(...)
    }
    render() {
    // create portal
    }

    View full-size slide

  132. class Menu extends React.Component {
    constructor() {
    document.getElementById('cart').innerHTML = ''
    }
    onClick() {...}
    render() {
    const cartEl = document.getElementById('cart')
    return
    ...
    { ReactDOM.unstable_createPortal(
    , cartEl) }

    }

    View full-size slide

  133. class Menu extends React.Component {
    constructor() {
    document.getElementById('cart').innerHTML = ''
    }
    onClick() {...}
    render() {
    const cartEl = document.getElementById('cart')
    return
    ...
    { ReactDOM.unstable_createPortal(
    , cartEl) }

    }

    View full-size slide

  134. class Menu extends React.Component {
    constructor() {
    document.getElementById('cart').innerHTML = ''
    }
    onClick() {...}
    render() {
    const cartEl = document.getElementById('cart')
    return
    ...
    { ReactDOM.unstable_createPortal(
    , cartEl) }

    }

    View full-size slide

  135. class Menu extends React.Component {
    constructor() {
    document.getElementById('cart').innerHTML = ''
    }
    onClick() {...}
    render() {
    const cartEl = document.getElementById('cart')
    return
    ...
    { ReactDOM.unstable_createPortal(
    , cartEl) }

    }

    View full-size slide

  136. class Menu extends React.Component {
    constructor() {
    document.getElementById('cart').innerHTML = ''
    }
    onClick() {...}
    render() {
    const cartEl = document.getElementById('cart')
    return
    ...
    { ReactDOM.unstable_createPortal(
    , cartEl) }

    }

    View full-size slide

  137. tomato
    Menu.js
    Sidebar.js

    View full-size slide

  138. tomato
    Menu.js
    Sidebar.js

    View full-size slide

  139. tomato
    Menu.js
    Sidebar.js

    View full-size slide

  140. yay
    state management

    View full-size slide

  141. yay
    state management
    without adding an external library

    View full-size slide

  142. yay
    state management
    without adding an external library
    or a lot of code!

    View full-size slide

  143. unstable_createPortal

    View full-size slide

  144. what’s
    new?
    why
    rewrite?
    ooh
    portals!

    View full-size slide

  145. @siddharthkp
    DM is open, say hi!

    View full-size slide

  146. @siddharthkp
    DM is open, say hi!
    bit.ly/react-training

    View full-size slide