Upload
others
View
0
Download
0
Embed Size (px)
Citation preview
Advanced React –Virtual DOM
AndPerformance
Medium: medium.com/@vzaidmanTwitter: twitter.com/vzaidmanGitHub: github.com/vzaidmanEmail: [email protected]
By: Vitali Zaidman
Web Architect and Blogger at Welldone Software• Work with React since it was released• Worked a lot on performance• Creator and (almost) the sole maintainer of why-did-you-render
Premature OptimizationPerformance is a feature!
Performance Matters!
In a study made by Google:
• Pinterest increased search engine traffic and sign-ups by 15% when they reduced perceived wait times by 40%
• COOK increased conversions by 7%, decreased bounce rates by 7%, and increased pages per session by 10% when they reduced average page load time by 850 milliseconds
* Make sure your product department is aware of this.
Performance In General
• Consider simulating the environment your users actually use in the real world.Remember that your machine is probably much stronger then that of your average user.
• Consider measuring performance and assigning a score to different versions.It's not easy but it's very valuable to find out if:• Performance actually improves.• You work on the most acute issues.
• Performance optimizations would come naturally to youonce you understand the processes of your application in depth.
Lecture FocusWhat usually cause performance issues:
• Network Issues - Lack of pagination, slow servers, wrong protocols, huge assets, caching, cdns, etc...
• Over-accessing the DOM
• CSS recalculations for huge layouts
• Over-bloating the DOM with elements, events and CSS rules
• iFrames, heavy libraries, and processes that runin parallel to the main application
What we will focus on:
• React Issues – slowness in React’s lifecycles
The DemoThought the lecture we would use the same demo with slight modifications.
React Terminology• React Component
Recipe for React on how to act with acertain part of the Virtual DOM.
• React ElementWhat we get whenReact.createElement(ReactComponent)or it's equivalent JSX version<ReactComponent/> are called.
They are immutable and representa “building block” of the Virtual DOM.
• React Component InstanceAn instance of React Component that is responsible for a React Element’s lifecycle in a specific part of the Virtual DOM.
Virtual DOMWe are going to look at an approximation of how the Virtual DOM works after React
components render to better grasp React best practices and deal with performance issues.
Virtual DOM’s ReconciliationOn a click on “Increase Counter”First step: Calculate a new “snapshot”* of the Virtual DOMby re-rendering all it’s components
Previous Virtual DOM snapshot Next Virtual DOM snapshot
* Not a real thing but helps in grasping the Virtual DOM
On a click on “Increase Counter”Second step: Calculate and update the Real DOM with the changesThird step: Dump these updates to the Real DOM in the most effective way
Virtual DOM’s Reconciliation
Previous Virtual DOM snapshot Next Virtual DOM snapshot
React Component’s lifecycleHow would the following code affect the lifecycle of the ChildComponent?
React Component’s lifecycleGIF Animation
React Component’s lifecycle
ParentComponent re-renders
“child” is a React Element – an immutablebuilding block of the Virtual DOM
GIF Animation
React Component’s lifecycle
ParentComponent re-renders here whenever Demo renders
Child renders here - onceGIF Animation
Reconciliation HeuristicsThere’s potentially a React anti-pattern here. Can you spot it?
GIF Animation
Reconciliation HeuristicsThe problem here is that the Child Component un-mounts and re-mounts on each render.Imagine how can it affect behaviors like Ajax requests in the real world.
GIF Animation
This is because the reconciliation uses a the react component’s hierarchy as aheuristic to keep React Component Instances.
Previous Virtual DOM snapshot Next Virtual DOM snapshot
Reconciliation Heuristics
When a React Element is created from a new React Component,The old instance is unmounted and the new one is mounted.
Reconciliation HeuristicsHow can we achieve this behavior without these re-renders?
GIF Animation
Reconciliation HeuristicsOnly by keeping the Virtual DOM’s hierarchy. (official React docs)
Side note: All The Ways To Be Re-Rendered• Create Element - <ReactComponent/> - React.createElement(ReactComponent)
When a React element is created it (re-)renders(Unless shouldComponentUpdate, React.memo prevent it)
• Set State - this.setState({ count: 0 })When a component changes it’s own state
• Force Update - this.forceUpdate()When a component changes it’s own state
• React Hooks
• useState, useReducer - When the hook’s value is changes
• useContext - When the Context’s value changes
Reconciliation PerformanceIn the example we saw before,When Demo re-renders,
“child” stays the same React ElementIn the new Virtual DOM snapshot.
Or in another words, it equals by reference:
previous child === next child
Reconciliation PerformanceParentComponent, on the other hand, isre-created.
In the new Virtual DOM snapshotit would be a new immutable React Element.
Reconciliation PerformanceWhat about the onClick function?
A new function is generated on each render.
React doesn’t change the event listener on the HTML button.
Instead, it changes the saved onClickfunction that is relayed from the same event listener.
Reconciliation PerformanceWhat if we do the famous trick of moving the function on the class?
The handleClick function is now generated only once, on the first mount of the component.
React never changes what onClick does anymore since
previous onClick === next onClick
Premature OptimizationWhile it saves us a function creation and some operations inside React on each render,
While it may come handy in many cases, in 97% of the cases, saves us so little, that we shouldn’t care.
This is usually a premature optimization.
Reconciliation PerformanceExtracting style objects in this fashion, saves us a deep comparison of each style obj on every render.
7 properties in our case.
It’s also usually a premature optimization.
Performance Issues
From: https://developers.google.com/web/fundamentals/performance/rail
So what actually causes performance issues?
Based on the RAIL Model from Google, we have 100ms from the user’s input to a renderso the user won’t feel a lag.
In these 100ms,these are out of our control:• Task queue• React lifecycle and reconciliation• Pain and layout calculations• Other browser’s housekeeping
So we have around less then ~50msfrom a triggered React input (like onClick)to finishing all the renders.
Virtual DOM’s Reconciliation and DOM update
Performance Issues“Very heavy operations” in React’s lifecycle• Algorithmic / Mathematical calculations• Manipulating data
like filtering, mapping and reducing
Performance IssuesPure components only re-render if one of their props change.
Performance IssuesWhat can we do with very heavy calculations?Memoization of Derived Data• Reselect in Redux - createSelector(selectUsers, selectStats…)• React Hooks - useMemo()• Lodash - _.memoize()• Mobx - @computed• And many others
Web Workers• Asynchronously calculate
in a parallel thread
Avoid Calculating• Calculate on server• Change the data structure
Performance IssuesMany operations in React’s lifecycle• When a change is made high in the application’s hierarchy• Many high order components
•
• When a component re-renders many times• When there’s a lot of instances of a component:
• Long lists• Graphics• Complex UI
Costly operations like:• Accessing the DOM directly• Creation of React Elements
Heavy OperationsRepeated “heavy operations” in React’s lifecycleNotice how re-rendering 10000 div elements takes a long time.Also notice it happens on every click
Pure components only re-render if any of their props changeRegular components re-render on any re-creation of the React element.
Heavy Operations
Pure Components
Can we make the ParentComponent pure instead of HugeList?
Pure Components
No! Because even if ChildComponent would be pure,It would always be a new React Element!
Pure Components
Passing props that are always different from previous props is an anti-pattern.
Not only these components always re-render, they also consume more processing power when testing if they need to re-render.
In some cases these components receive many props and are used across the application.
Pure Components
Why Did You Render?
When React is Not The AnswerHere, instead of using React, I draw a million of pixels using an “offscreenCanvas” which is a canvas that can be used through a webworker. It re-draws every 300ms at-most.
Huge Components
Huge Components
Huge ComponentsBreaking a component into many smaller pure components ensure smaller parts of it actually re-render.
Virtualized ListsMake sure to use virtual lists to only render the rows that are actually seen on the screen.This technique allows displaying lists with thousands of rows.
Check out this great lecture by Brian Vaughn for more information.
Source: https://bvaughn.github.io/forward-js-2017/
Conclusion• Do not optimize prematurely and focus on what’s important
• Always consider what needs to be rendered
• Try to avoid, memoize, or do heavy calculations in web workers.
• Break components into smaller pure components
• Consider using tools other than React for cpecific goals
• See if libraries like React Virtualized already have a solution
Medium: medium.com/@vzaidmanTwitter: twitter.com/vzaidmanGitHub: github.com/vzaidmanEmail: [email protected]
By: Vitali Zaidman
Web Architect and Blogger at Welldone Software
Thank You!