Performance

on

FrontEnd Dev

by Alfian Busyro

Goals

  • Aware of performance impact from a line of code
  • Produce a better performant code for the better application
  • Reduce potentially buggy code

Agenda

  • Reflow and repaint
  • React mechanism
  • Render blocking
  • Async process (Promise, rAF, setTimeout, etc)
  • Debugging performance of React App
  • Hooks (if we have plenty of time)

Reflow and Repaint

Repaint - also known as redraw - is what happens whenever something is made visible when it was not previously visible, or vice versa, without altering the layout of the document.

- Opera

What is reflow ?

DOM changes will trigger reflow,

which will trigger browser to calculate the layout

Less repaint and reflow

equal to more optimized

React

Virtual Dom

How to avoid repaint in React

  • add key to your list (looped component)
class MyComponent extends React.PureComponent {
  render() {
    const { data } = this.props

    return (
      <ul>
         {data.map((item, i) => {
              return <li key={item.id}>{item.title}</li>
        })}
     </ul>
    )
  }
}

How to avoid repaint in React

  • use PureComponent or guard with shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState) {
  return (
    (!isEqual(this.props.a, nextProps.a)) ||
    (!isEqual(this.state.data, nextState.data))
  )
}
// From

export default class AwesomeComponent extends React.Component {

// To

export default class AwesomeComponent extends React.PureComponent {

How to avoid reflow in React

  • don't access and change the DOM directly

If you have please use ref  instead

https://reactjs.org/docs/refs-and-the-dom.html

Recommendation read

about reflow and repaint

Debugging reflow and repaint

https://github.com/maicki/why-did-you-update

React Mechanism

Understanding Modern React's Life Cycle

Quiz

class HelloReact extends React.Component {
  
  state = {
    a: 1, b: 2
  }

  increase = async () => {
    const increaseStateVal = async () => {
       const {a, b} = this.state
      this.setState({
        a: a+1,
        b: b+1,
      })
    }
    await increaseStateVal()
  }
  
  render() {
    const {a, b} = this.state
    this.increase()
    return (
      <>
        <div>a: {a}</div>
        <div>b: {b}</div>
      </>
    )
}}

What we learnt from the life cycle

  • Do initialization in constructor
  • If you have big heavy init process, do it in cDM or cDU
  • avoid to use getDerivedStateFromProps. Please use cDU (componentDidUpdate) for alternative.

https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

Render Blocking

JavaScript is single thread non blocking programming language

The Callstack

one thread -- one callstack -- one thing at a time

export default class AwesomeComponent extends Component {

  glamorousAsyncProcess() {
    // heavy redux action
  }

  constructor() {
    super()
    // don't
    this.glamorousAsyncProcess()
  }

  getDerivedStateFromProps(props, state) {
    // don't
    this.glamorousAsyncProcess()
  }

  async populateUser() {
    // populate 1M Users
    await this.props.populateMillionUsers()
  }

  render() {
    //don't
    this.populateUser()
    
    return(
      <div>
        <span>
          Hello World! with heavy process on the back
        </span>
      </div>
    )
  }

}
class AwesomeComponent extends Component {

  glamorousAsyncProcess() {
    // heavy redux action
  }

  constructor() {
    super()
    this.glamorousAsyncProcess = this.glamorousAsyncProcess.bind(this)
  }

  componentDidMount() {
    // do
    this.glamorousAsyncProcess()
  }

  componentDidUpdate() {
    // do
    this.glamorousAsyncProcess()
  }

  render() {
    return(
      <div>
        <span>
          Hello World! with heavy process on the back
        </span>
        <button onClick={this.viewContacts} >
          Press Me!
        </button>
      </div>
    )
  }
}

Asynchronous Process

Promise

is 

a MicroTask

Async - await

is a Promise ....

So it's a Microtask

Function generator (*func)

is a Promise ....

So it's a Microtask

setTimeout

will run after an event loop done

The Event Loop

Recommendation

requestAnimationFrame

deferred process until the next paint on JS Thread

Codes

import nextFrame from 'next-frame';

// ...

for (let recording of recordingsJSON) {
  await nextFrame(); // This is all we need to add!
  mergeRecordingToLocalDatabase(recording);
}

Debugging performance of React App​

Demo

React Hooks

Made with Slides.com