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.

Ea26ebbe66c3bb6afb5f711cfe766dff?s=128

Siddharth Kshetrapal

September 14, 2017
Tweet

Transcript

  1. A portal to the future!

  2. Siddharth Kshetrapal

  3. sidconf

  4. @siddharthkp

  5. @siddharthkp javascript architect

  6. @siddharthkp design, javascript, web performance

  7. None
  8. None
  9. bit.ly/siddharthkpshow

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

  11. co-organise react bangalore

  12. i have stickers grab them right outside

  13. A portal to the future!

  14. is Fiber ready yet?

  15. http://isfiberreadyyet.com

  16. what is fiber?

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

  18. The diffing algorithm React uses to tell which parts need

    to be changed
  19. what does this mean for us? React 16

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

  21. None
  22. 4 tiny complaints

  23. None
  24. <h1>Hello!</h1> <p>I'm Siddharth</p>

  25. const MyComponent = () => { return ( <h1>Hello!</h1> <p>I'm

    Siddharth</p> ) }
  26. None
  27. const MyComponent = () => { return ( <h1>Hello!</h1> <p>I'm

    Siddharth</p> ) }
  28. const MyComponent = () => { return ( <div> <h1>Hello!</h1>

    <p>I'm Siddharth</p> </div> ) }
  29. const MyComponent = () => { return ( <tr><td>Item 1</td></tr>

    <tr><td>Item 2</td></tr> ) }
  30. const MyComponent = () => { return ( <div> <tr><td>Item

    1</td></tr> <tr><td>Item 2</td></tr> </div> ) }
  31. None
  32. const MyComponent = () => { return ( <div> <tr><td>Item

    1</td></tr> <tr><td>Item 2</td></tr> </div> ) }
  33. const MyComponent = () => { return ( [ <tr><td>Item

    1</td></tr>, <tr><td>Item 2</td></tr> ] ) }
  34. None
  35. None
  36. None
  37. None
  38. componentDidCatch(error, info)

  39. error boundaries try/catch, but for components

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

    return ( <ErrorBoundary> <BuggyComponent /> </ErrorBoundary> ) }
  41. import React from 'react' const MyComponent = () => {

    return ( <ErrorBoundary> <BuggyComponent /> </ErrorBoundary> ) }
  42. 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 <h1>Something broke </h1> else return {this.props.children} } }
  43. 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 <h1>Something broke </h1> else return {this.props.children} } }
  44. 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 <h1>Something broke </h1> else return {this.props.children} } }
  45. 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 <h1>Something broke </h1> else return {this.props.children} } }
  46. 500

  47. None
  48. ReactDOM.renderToString server side rendering

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

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

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

  52. None
  53. import { renderToString } from 'react-dom/server' server.get('/', (req, res) =>

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

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

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

    { const html = renderToString(<App />) res.end(html) })
  57. ReactDOM.renderToNodeStream

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

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

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

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

    { renderToNodeStream(<App />) .pipe(res) .on('end', () => res.end()) })
  62. None
  63. $ npm install react@next react-dom@next

  64. None
  65. why rewrite?

  66. better scheduling of work

  67. Filter users

  68. s

  69. si

  70. sid

  71. sidd

  72. siddh

  73. siddha

  74. siddhar

  75. siddhart

  76. siddharth

  77. Filter users

  78. Filter users

  79. s

  80. s

  81. siddh

  82. siddh

  83. siddharth

  84. Filter users HUMAN JAVASCRIPT DOM

  85. s Filter users

  86. s Filter users keydown event 's'

  87. s s keydown event 's' repaint

  88. s i s

  89. s i s

  90. s i si

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

  92. Filter users

  93. s s

  94. s s render filtered list

  95. s i s

  96. s i s

  97. s i si

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

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

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

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

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

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

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

  105. siddh

  106. siddh

  107. split work at component boundaries

  108. cooperative multitasking

  109. None
  110. None
  111. None
  112. /* node_modules/react-dom/cjs/react-dom.development.js /* var ReactDOMFeatureFlags = { fiberAsyncScheduling: false, useFiber:

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

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

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

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

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

    }) }) /* this is a low priority task /*
  118. None
  119. None
  120. PORTALS

  121. ReactDOM.render(<RootComponent/>, target)

  122. ReactDOM.createPortal(<Component/>, target)

  123. real world problem #1

  124. tomato

  125. const html = ReactDOM.renderToString(<App/>)

  126. const html = ReactDOM.renderToString(<App/>) res.send(` <div id="root">${html}</div> `)

  127. const html = ReactDOM.renderToString(<App/>) res.send(` <div id="root">${html}</div> `)

  128. const html = ReactDOM.renderToString(<App/>) res.send(` <div id="root">${html}</div> `) ReactDOM.hydrate( <App/>,

    document.getElementById('root') )
  129. tomato

  130. tomato

  131. tomato

  132. tomato

  133. tomato dynamic dynamic

  134. tomato

  135. const html = ReactDOM.renderToString(<App/>) res.send(` <div id="root">${html}</div> `) ReactDOM.hydrate( <App/>,

    document.getElementById('root') )
  136. const html = ReactDOM.renderToString(<App/>) res.send(` <div id="root">${html}</div> `) ReactDOM.hydrate( <Client/>,

    document.getElementById('root') )
  137. tomato

  138. tomato

  139. const html = ReactDOM.renderToString(<App/>) res.send(` <div id="root">${html}</div> `) ReactDOM.hydrate( <Client/>,

    document.getElementById('root') )
  140. const html = ReactDOM.renderToString(<App/>) res.send(` <div id="root">${html}</div> <div id="client-root"></div> `)

    ReactDOM.hydrate( <Client/>, document.getElementById('root') )
  141. const html = ReactDOM.renderToString(<App/>) res.send(` <div id="root">${html}</div> <div id="client-root"></div> `)

    ReactDOM.hydrate( <Client/>, document.getElementById('client-root') )
  142. const html = ReactDOM.renderToString(<App/>) res.send(` <div id="root">${html}</div> <div id="client-root"></div> `)

    ReactDOM.hydrate( <Client/>, document.getElementById('client-root') )
  143. class Client extends React.Component { }

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

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

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

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

    '' } render() { return <div> </div> } }
  148. class Client extends React.Component { constructor() { super() document.getElementById('cart').innerHTML =

    '' } render() { const cartEl = document.getElementById('cart') return <div> </div> } }
  149. class Client extends React.Component { constructor() { super() document.getElementById('cart').innerHTML =

    '' } render() { const cartEl = document.getElementById('cart') return <div> { ReactDOM.unstable_createPortal(<Cart />, cartEl) } </div> } }
  150. tomato

  151. tomato

  152. tomato

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

    '' } render() { const cartEl = document.getElementById('cart') return <div> { ReactDOM.unstable_createPortal(<Cart />, cartEl) } </div> } }
  154. class Client extends React.Component { constructor() { super() document.getElementById('cart').innerHTML =

    '' document.getElementById('menu').innerHTML = '' } render() { const cartEl = document.getElementById('cart') return <div> { ReactDOM.unstable_createPortal(<Cart />, cartEl) } </div> } }
  155. class Client extends React.Component { constructor() { super() document.getElementById('cart').innerHTML =

    '' document.getElementById('menu').innerHTML = '' } render() { const cartEl = document.getElementById('cart') return <div> { ReactDOM.unstable_createPortal(<Cart />, cartEl) } </div> } }
  156. 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 <div> { ReactDOM.unstable_createPortal(<Cart />, cartEl) } </div> }
  157. 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 <div> { ReactDOM.unstable_createPortal(<Cart />, catdEl) } { ReactDOM.unstable_createPortal(<Menu />, menuEl) } </div>
  158. tomato

  159. tomato

  160. tomato

  161. yay, performance!

  162. real world problem #2

  163. tomato

  164. tomato

  165. tomato

  166. tomato

  167. tomato state: { items: [ { id: 1, count: 0

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    }, { id: 2, count: 1 }, ...]} o Sidebar.js onClick = (item) => this.setState(...) App.js Menu.js
  185. 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
  186. 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
  187. None
  188. tomato { items: [ { id: 1, count: 0 },

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

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

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

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

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

    { id: 2, count: 1 }, ...] } Store Sidebar.js Menu.js
  194. None
  195. tomato Menu.js Sidebar.js state: { items: [ { id: 1,

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

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

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

    count: 0 }, { id: 2, count: 1 }, ... ]} onClick = (item) => { this.setState(...) } render() { // create portal }
  199. class Menu extends React.Component { constructor() { document.getElementById('cart').innerHTML = ''

    } onClick() {...} render() { const cartEl = document.getElementById('cart') return <div> ... { ReactDOM.unstable_createPortal( <Cart items={this.state.items} />, cartEl) } </div> }
  200. class Menu extends React.Component { constructor() { document.getElementById('cart').innerHTML = ''

    } onClick() {...} render() { const cartEl = document.getElementById('cart') return <div> ... { ReactDOM.unstable_createPortal( <Cart items={this.state.items} />, cartEl) } </div> }
  201. class Menu extends React.Component { constructor() { document.getElementById('cart').innerHTML = ''

    } onClick() {...} render() { const cartEl = document.getElementById('cart') return <div> ... { ReactDOM.unstable_createPortal( <Cart items={this.state.items} />, cartEl) } </div> }
  202. class Menu extends React.Component { constructor() { document.getElementById('cart').innerHTML = ''

    } onClick() {...} render() { const cartEl = document.getElementById('cart') return <div> ... { ReactDOM.unstable_createPortal( <Cart items={this.state.items} />, cartEl) } </div> }
  203. class Menu extends React.Component { constructor() { document.getElementById('cart').innerHTML = ''

    } onClick() {...} render() { const cartEl = document.getElementById('cart') return <div> ... { ReactDOM.unstable_createPortal( <Cart items={this.state.items} />, cartEl) } </div> }
  204. tomato Menu.js Sidebar.js

  205. tomato Menu.js Sidebar.js

  206. tomato Menu.js Sidebar.js

  207. yay state management

  208. yay state management without adding an external library

  209. yay state management without adding an external library or a

    lot of code!
  210. unstable_createPortal

  211. None
  212. what’s new? why rewrite? ooh portals!

  213. @siddharthkp DM is open, say hi!

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