[Translation] application of the [Advanced] Throttle and Debounce 6-5 in the React

Benpian is a translation, the original is Improve Your React App Performance by Using Throttling and Debouncing

introduction

When using React to build applications, we always encounter some limitations, such as the large number of calls, asynchronous network requests and DOM updates, we can use the functions provided React to check these.

  • shouldComponentUpdate(...) Lifecycle hook
  • React.PureComponent
  • React.memo
  • Windowing and Virtualization
  • Memoization
  • Hydration
  • Hooks (useState, useMemo, useContext, useReducer, 等)

In this article, we will examine how not to use the function provided down React React improved application performance, we will use a technology not just for React: throttle ( Throttle ) and image stabilization ( Debounce ).

Start with an example

Example 1

The following example may well explain throttle and the benefits of image stabilization, suppose we have a autocompcomponent

import React from 'react';
import './autocomp.css';
复制代码
class autocomp extends React.Component {
    constructor(props) {
        super(props);
        this.state= {
            results: []
        }
    }
复制代码
    handleInput = evt => {
        const value = evt.target.value
        fetch(`/api/users`)
            .then(res => res.json())
            .then(result => this.setState({ results: result.users }))
    }
复制代码
    render() {
        let { results } = this.state;
        return (
            <div className='autocomp_wrapper'>
            <input placeholder="Enter your search.." onChange={this.handleInput} />
            <div>
                {results.map(item=>{item})}
            </div>
            </div>
        );
    }
}
export default autocomp;
复制代码

In our autocompassembly, once we enter a word in the input box, it will request api/usersa list of users to be displayed. After each letter input trigger asynchronous network request, and after successful this.setStateupdate DOM.

Now, imagine an input fidudusolatry to search results fidudusolanke, there will be many names fidudusolaappear together.

1.  f
2.  fi
3.  fid
4.  fidu
5.  fidud
6.  fidudu
7.  fidudus
8.  fiduduso
9.  fidudusol
10. fidudusola
复制代码

This name has 10 letters, so we will have 10 times and 10 times DOM API request updated, this is just a user input eventually see it !! we expected after the completion of the names fidudusolankeand other results appear together.

Even autocompcan be done in the absence of network requests (for example, a local memory "database"), still need to be expensive DOM updates for each character / word input.

const data = [
    {
        name: 'nnamdi'
    },
    {
        name: 'fidudusola'
    },
    {
        name: 'fashola'
    },
    {
        name: 'fidudusolanke'
    },
    // ... up to 10,000 records
]
复制代码
class autocomp extends React.Component {
    constructor(props) {
        super(props);
        this.state= {
            results: []
        }
    }
复制代码
    handleInput = evt => {
        const value = evt.target.value
        const filteredRes = data.filter((item)=> {
            // algorithm to search through the `data` array
        })
        this.setState({ results: filteredRes })
    }
复制代码
    render() {
        let { results } = this.state;
        return (
            <div className='autocomp_wrapper'>
                <input placeholder="Enter your search.." onChange={this.handleInput} />
                <div>
                    {results.map(result=>{result})}
                </div>
            </div>
        );
    }
}
复制代码

Example 2

Another example is the use resizeand scrollother events. In most cases, the site rolling 1000 times per second, Imagine scrolladd an event handler event.

document.body.addEventListener('scroll', ()=> {
    console.log('Scrolled !!!')
})
复制代码

You will find this function is performed 1000 times per second! If this event handler to perform a lot of calculations or a large number of DOM operations, will face a worst-case scenario.

function longOp(ms) {
    var now = Date.now()
    var end = now + ms
    while(now < end) {
        now = Date.now()
    }
}
复制代码
document.body.addEventListener('scroll', ()=> {
    // simulating a heavy operation
    longOp(9000)
    console.log('Scrolled !!!')
})
复制代码

We have a 9 seconds to complete the operation requires, the final output Scrolled !!!, assuming we have more than 200 rolling 5000px event is triggered. Therefore, nine seconds to complete one event, about 9 * 200 = 1800s to run all 200 events. Thus, completed 30 minutes (half hour).

I am sure I will find a lag-free response and the browser, so the event handler written best be implemented within a short period of time.

Then we found that this will have a huge performance bottleneck in our application, we do not need to perform DOM API requests and updates on each letter entered, we need to wait until the user stops input or enter a period of time, or until the user stops scrolling after rolling for some time, go to the event handler function.

All of these ensure that our application has good performance, let's look at how to use the throttle and image stabilization to avoid this performance bottleneck.

Throttle Throttle

The maximum number of expansion force over time in a function that can be called, for example, once every 100 milliseconds, most functions performed.

Throttle means to perform a given function within a specified time. This limits the number of times the function is called, so the repeat function call does not reset any data.

Suppose we usually call the function at a rate of 1000 times / 20 seconds. If we use throttling to limit it to once every 500 milliseconds, we will see the functions to be performed within 20 seconds 40 times.

1000 * 20 secs = 20,000ms
20,000ms / 500ms = 40 times
复制代码

This is a great optimization from 40 to 1000 times.

The following example describes the use of a throttle in React, respectively using underscore, lodash, RxJSand custom implementations.

Use underscore

We will use the underscorethrottle function provides a handle our autocompassemblies.

Install dependencies.

npm i underscore
复制代码

It is then introduced in the assembly:

// ...
import * as _ from underscore;
复制代码
class autocomp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            results: []
        }
       this.handleInputThrottled = _.throttle(this.handleInput, 1000)
    }
复制代码
    handleInput = evt => {
        const value = evt.target.value
        const filteredRes = data.filter((item)=> {
            // algorithm to search through the `data` array
        })
        this.setState({ results: filteredRes })
    }
复制代码
    render() {
        let { results } = this.state;
        return (
            <div className='autocomp_wrapper'>
                <input placeholder="Enter your search.." onChange={this.handleInputThrottled} />
                <div>
                    {results.map(result=>{result})}
                </div>
            </div>
        );
    }
}
复制代码

Throttling function receives two parameters, respectively, as a function of time and the difference needs to be limited, a function that returns the throttling process. In our example, handleInputthe method is passed to the throttlefunction, the time difference is 1000ms.

Now, suppose we enter at the normal speed every 200ms 1 letter fidudusola, input required to complete 10 * 200ms = (2000ms) 2s , then handleInputthe method will be called only 2 (2000ms / 1000ms = 2) times instead of the original 10 times.

Use lodash

lodashAlso provides a throttlefunction, we can use it in JS program.

First, we need to rely on.

npm i lodash
复制代码

Use lodashour autocompwill is this.

// ...
import { throttle } from lodash;
复制代码
class autocomp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            results: []
        }
       this.handleInputThrottled = throttle(this.handleInput, 100)
    }
复制代码
    handleInput = evt => {
        const value = evt.target.value
        const filteredRes = data.filter((item)=> {
            // algorithm to search through the `data` array
        })
        this.setState({ results: filteredRes })
    }
复制代码
    render() {
        let { results } = this.state;
        return (
            <div className='autocomp_wrapper'>
                <input placeholder="Enter your search.." onChange={this.handleInputThrottled} />
                <div>
                    {results.map(result=>{result})}
                </div>
            </div>
        );
    }
}
复制代码

And underscorethe same effect, there is no other difference.

Use RxJS

JS is Reactive Extensionsprovided a throttle operator, we can use it to implement the functions.

First, we installed rxjs.

npm i rxjs
复制代码

We from rxjsimport librarythrottle

// ...
import { BehaviorSubject } from 'rxjs';
import { throttle } from 'rxjs/operators';
复制代码
class autocomp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            results: []
        }
        this.inputStream = new BehaviorSubject()
    }
复制代码
    componentDidMount() {
        this.inputStream
            .pipe(
                throttle(1000)
            )
            .subscribe(v => {
                const filteredRes = data.filter((item)=> {
                    // algorithm to search through the `data` array
                })
                this.setState({ results: filteredRes })
        })
    }

复制代码
    render() {
        let { results } = this.state;
        return (
            <div className='autocomp_wrapper'>
                <input placeholder="Enter your search.." onChange={e => this.inputStream.next(e.target.value)} />
                <div>
                    {results.map(result => { result })}
                </div>
            </div>
        );
    }
}

复制代码

We from rxjsintroduced in throttleand BehaviorSubjectinitializing an BehaviorSubjectinstance stored in the inputStreamattribute, in componentDidMount, we will inputStreamflow to the throttle operator, the incoming 1000, showing a throttle control RxJS 1000ms, the operation returns to the flow is throttled to subscribe obtain the current value.

Because subscribed inputStream when the component is loaded, so when we start typing, the content is sent to the input inputStreamstream. In the beginning, because the throttleoperation does not send the contents character 1000ms, send the latest value after that, they start to get the results after sending.

If we 200ms 1 letter input speed fidudusola, the assembly will re-render 2000ms / 1000ms = 2 times.

Use custom implementation

How do we achieve their throttling function to facilitate a better understanding of the throttle work.

We know that the function of a throttle control, it will call the specified time interval, we will use the setTimeout function to achieve this.

function throttle(fn, ms) {
    let timeout
    function exec() {
        fn.apply()
    }
    function clear() {
        timeout == undefined ? null : clearTimeout(timeout)
    }
    if(fn !== undefined && ms !== undefined) {
        timeout = setTimeout(exec, ms)
    } else {
        console.error('callback function and the timeout must be supplied')
    }
    // API to clear the timeout
    throttle.clearTimeout = function() {
        clear();
    }
}
复制代码

The above implementation is very simple, direct use setTimeout API implemented in React project follows.

Note: The above text realize there is a problem, detailed implementation and parsing the throttle function can view my other article, click to see

// ...
class autocomp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            results: []
        }
       this.handleInputThrottled = throttle(this.handleInput, 100)
    }
复制代码
    handleInput = evt => {
        const value = evt.target.value
        const filteredRes = data.filter((item)=> {
            // algorithm to search through the `data` array
        })
        this.setState({ results: filteredRes })
    }
复制代码
    render() {
        let { results } = this.state;
        return (
            <div className='autocomp_wrapper'>
                <input placeholder="Enter your search.." onChange={this.handleInputThrottled} />
                <div>
                    {results.map(result=>{result})}
                </div>
            </div>
        );
    }
}
复制代码

Image stabilization Debounce

Since the last image stabilization forces will be called again after a certain time after the function call, for example, only in the case it has not been invoked after a period of time (for example, 100 milliseconds) before executing the function.

When image stabilization, it ignores all calls to the function, the function stop calling until after some time to execute again.

The following describes an example of using debounce in the project.

Use underscore

// ...
import * as _ from 'underscore';
class autocomp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            results: []
        }
       this.handleInputThrottled = _.debounce(this.handleInput, 100)
    }
    handleInput = evt => {
        const value = evt.target.value
        const filteredRes = data.filter((item)=> {
            // algorithm to search through the `data` array
        })
        this.setState({ results: filteredRes })
    }
    render() {
        let { results } = this.state;
        return (
            <div className='autocomp_wrapper'>
                <input placeholder="Enter your search.." onChange={this.handleInputThrottled} />
                <div>
                    {results.map(result=>{result})}
                </div>
            </div>
        );
    }
}
复制代码

Use lodash

// ...
import { debounce } from 'lodash';
class autocomp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            results: []
        }
       this.handleInputThrottled = debounce(this.handleInput, 100)
    }
    handleInput = evt => {
        const value = evt.target.value
        const filteredRes = data.filter((item)=> {
            // algorithm to search through the `data` array
        })
        this.setState({ results: filteredRes })
    }
    render() {
        let { results } = this.state;
        return (
            <div className='autocomp_wrapper'>
                <input placeholder="Enter your search.." onChange={this.handleInputThrottled} />
                <div>
                    {results.map(result=>{result})}
                </div>
            </div>
        );
    }
}
复制代码

Use RxJS

// ...
import { BehaviorSubject } from 'rxjs';
import { debounce } from 'rxjs/operators';
class autocomp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            results: []
        }
        this.inputStream = new BehaviorSubject()
    }
    componentDidMount() {
        this.inputStream
            .pipe(
                debounce(100)
            )
            .subscribe(v => {
                const filteredRes = data.filter((item)=> {
                    // algorithm to search through the `data` array
                })
                this.setState({ results: filteredRes })
        })
    }
    render() {
        let { results } = this.state;
        return (
            <div className='autocomp_wrapper'>
                <input placeholder="Enter your search.." onChange={e => this.inputStream.next(e.target.value)} />
                <div>
                    {results.map(result => { result })}
                </div>
            </div>
        );
    }
}
复制代码

Important areas: game

There are many cases need to use the throttle and image stabilization, these areas need most is game. Game action is the most commonly used key on a computer keyboard or game controller, the player may always press the same key several times (40 times per 20 seconds, i.e., twice per second) shot e.g., accelerate such an operation, but both players by number of key shots down the number, it will only launch once (for example, per second). Therefore, use of a throttle control is 1 second, so that the second button press will be ignored.

in conclusion

We saw how to improve throttle and anti-shake performance React applications, as well as repeated calls to affect performance, because the components and their sub-trees would unnecessarily re-rendered, so it should avoid duplication invoke methods React applications. In a small program does not attract attention, but in large programs, the effect will be very obvious.

After reading three things ❤️

If you find this content you quite inspiring, I want to invite you to help me three small favor:

  1. Thumbs up , so that more people can see this content ( collection point no praise, bullying -_- )
  2. Follow me on GitHub , so that we become a long-term relationship
  3. No public concern, "senior front-end Advanced" , a weekly focusing on capturing heavy and difficult front-end interview, the public number backstage reply "Information" send you high-quality selection of front-end data.

Reproduced in: https: //juejin.im/post/5d0a5365f265da1b5d57b0ab

Guess you like

Origin blog.csdn.net/weixin_33768481/article/details/93166134