Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
October 1, 2020 10:34 am GMT

How to use debouncing to Improve the performance of the search functionality

In this article, we will see how to use debouncing to improve the performance of search functionality in the application.

If we're making an API call to the server for every character typed in the input search box and the data returned by API contains a lot of data let's say 500 or 1000 user records then it will slow down your application.

Because for every character typed in the search box, we're making an API call and the server may take some time to return data and before the server returns the data we're making another API call for the next character typed.

In almost every application we need to add some form of search functionality may be to filter some records or get the result from API.

So this is a common scenario and making an API call for every character typed may be expensive because some third-party applications or cloud providers like firebase, AWS, etc provide only a limited number of API requests, and cost is incurred for every additional request.

So to handle this scenario, we can use the debouncing functionality.

Let's first understand what is debouncing.

Debouncing allows us to call afunction after a certain amount of time has passed. This is very useful to avoidunnecessary API calls to the server If you're making an API call for every character typed in the input text.

Let's understand this by writing some code.

Without Debouncing in class component

import React from 'react';import axios from 'axios';import { Form } from 'react-bootstrap';export default class WithoutDebouncingClass extends React.Component {  state = {    input: '',    result: [],    errorMsg: '',    isLoading: false  };  handleInputChange = (event) => {    const input = event.target.value;    this.setState({ input, isLoading: true });    axios      .get(`https://www.reddit.com/search.json?q=${input}`)      .then((result) => {        this.setState({          result: result.data.data.children,          errorMsg: '',          isLoading: false        });      })      .catch(() => {        this.setState({          errorMsg: 'Something went wrong. Try again later.',          isLoading: false        });      });  };  render() {    const { input, result, errorMsg, isLoading } = this.state;    return (      <div className="container">        <div className="search-section">          <h1>Debouncing Demo</h1>          <Form>            <Form.Group controlId="search">              <Form.Control                type="search"                placeholder="Enter text to search"                onChange={this.handleInputChange}                value={input}                autoComplete="off"              />            </Form.Group>            {errorMsg && <p>{errorMsg}</p>}            {isLoading && <p className="loading">Loading...</p>}            <ul className="search-result">              {result.map((item, index) => (                <li key={index}>{item.data.title}</li>              ))}            </ul>          </Form>        </div>      </div>    );  }}

In the above code, we're displaying a search box where user types some value and we're calling the handleInputChange method on the onChange event of the input text box.

Inside that method, we're making an API call to reddit by passing the search string and we're storing the result in the results array in the state and displaying the result as an unordered list.

Class without

As you can see, on every character typed, we are making an API call. So we are unnecessarily increasing the server API calls.

If the server is taking more time to return the data, you might see the previous result even when you are expecting new results based on your input value.

To fix this, we can use debouncing where we only make an API request after half-second(500 milliseconds) once a user has stopped typing which is more beneficial. It will save from unnecessary requests and will also save from previous API call result being displayed for a short time.

With debouncing in class component

Here, we will use the debounce method provided by lodash library to add the debouncing functionality.

import React from 'react';import axios from 'axios';import _ from 'lodash';import { Form } from 'react-bootstrap';export default class WithDebouncingClass extends React.Component {  constructor(props) {    super(props);    this.state = {      input: '',      result: [],      errorMsg: '',      isLoading: false    };    this.handleSearchText = _.debounce(this.onSearchText, 500);  }  onSearchText = (input) => {    this.setState({ isLoading: true });    axios      .get(`https://www.reddit.com/search.json?q=${input}`)      .then((result) => {        this.setState({          result: result.data.data.children,          errorMsg: '',          isLoading: false        });      })      .catch(() => {        this.setState({          errorMsg: 'Something went wrong. Try again later.',          isLoading: false        });      });  };  handleInputChange = (event) => {    const input = event.target.value;    this.setState({ input });    this.handleSearchText(input);  };  render() {    const { input, result, errorMsg, isLoading } = this.state;    return (      <div className="container">        <div className="search-section">          <h1>Debouncing Demo</h1>          <Form>            <Form.Group controlId="search">              <Form.Control                type="search"                placeholder="Enter text to search"                onChange={this.handleInputChange}                value={input}                autoComplete="off"              />            </Form.Group>            {errorMsg && <p>{errorMsg}</p>}            {isLoading && <p className="loading">Loading...</p>}            <ul className="search-result">              {result.map((item, index) => (                <li key={index}>{item.data.title}</li>              ))}            </ul>          </Form>        </div>      </div>    );  }}

The lodash's debounce method accepts two parameters.

  • A function to execute
  • The number of milliseconds to wait before executing the passed function
this.handleSearchText = _.debounce(this.onSearchText, 500);

The debounce method returns a function which we stored in this.handleSearchText class variable and we're calling it in handleInputChange handler which gets called when the user types something in the input search textbox.

When we call the handleSearchText method, it internally calls the onSearchText method where we're making an API call to reddit.

Note that, we're calling the debounce function inside the constructor because this initialization needs to be done only once.

With Class

As you can see, with the added debouncing functionality, the API call is only made after half-second(500 milliseconds) when we stopped typing, thereby reducing the number of API calls and also the result is not flickered and we're getting only the final result which is expected and useful behavior.

If you're using React hooks, then you can't use the same code as a class to implement the debouncing. So let's see how to add debouncing in code using hooks.

Without Debouncing in React hooks

Let's first write the code without debouncing using hooks

import React, { useState } from 'react';import axios from 'axios';import { Form } from 'react-bootstrap';const WithoutDebouncingHooks = () => {  const [input, setInput] = useState('');  const [result, setResult] = useState([]);  const [errorMsg, setErrorMsg] = useState('');  const [isLoading, setIsLoading] = useState(false);  const handleInputChange = (event) => {    const input = event.target.value;    setInput(input);    setIsLoading(true);    axios      .get(`https://www.reddit.com/search.json?q=${input}`)      .then((result) => {        setResult(result.data.data.children);        setErrorMsg('');        setIsLoading(false);      })      .catch(() => {        setErrorMsg('Something went wrong. Try again later.');        setIsLoading(false);      });  };  return (    <div className="container">      <div className="search-section">        <h1>Debouncing Demo</h1>        <Form>          <Form.Group controlId="search">            <Form.Control              type="search"              placeholder="Enter text to search"              onChange={handleInputChange}              value={input}              autoComplete="off"            />          </Form.Group>          {errorMsg && <p>{errorMsg}</p>}          {isLoading && <p className="loading">Loading...</p>}          <ul className="search-result">            {result.map((item, index) => (              <li key={index}>{item.data.title}</li>            ))}          </ul>        </Form>      </div>    </div>  );};export default WithoutDebouncingHooks;

This is the same code of debouncing without class written using hooks.

Let's see how we can add debouncing to this code.

With debouncing in React hooks

import React, { useState, useEffect, useRef } from 'react';import axios from 'axios';import _ from 'lodash';import { Form } from 'react-bootstrap';const WithDebouncingHooks = () => {  const [input, setInput] = useState('');  const [result, setResult] = useState([]);  const [errorMsg, setErrorMsg] = useState('');  const [isLoading, setIsLoading] = useState(false);  const inputRef = useRef();  useEffect(() => {    // initialize debounce function to search once user has stopped typing every half second    inputRef.current = _.debounce(onSearchText, 500);  }, []);  const onSearchText = (input) => {    setIsLoading(true);    axios      .get(`https://www.reddit.com/search.json?q=${input}`)      .then((result) => {        setResult(result.data.data.children);        setErrorMsg('');        setIsLoading(false);      })      .catch(() => {        setErrorMsg('Something went wrong. Try again later.');        setIsLoading(false);      });  };  const handleInputChange = (event) => {    const input = event.target.value;    setInput(input);    inputRef.current(input);  };  return (    <div className="container">      <div className="search-section">        <h1>Debouncing Demo</h1>        <Form>          <Form.Group controlId="search">            <Form.Control              type="search"              placeholder="Enter text to search"              onChange={handleInputChange}              value={input}              autoComplete="off"            />          </Form.Group>          {errorMsg && <p>{errorMsg}</p>}          {isLoading && <p className="loading">Loading...</p>}          <ul className="search-result">            {result.map((item, index) => (              <li key={index}>{item.data.title}</li>            ))}          </ul>        </Form>      </div>    </div>  );};export default WithDebouncingHooks;

In this code, we're calling the debounce function inside the useEffect hook by passing an empty array [] as a second argument because this code needs to be executed only once.

And we're storing the result of the function in inputRef.current. inputRef is a ref created by calling useRef() hook. It contains a current property which we can use to retain the value even after the component is re-rendered.

Using the local variable to store the result of debounce function will not work because for every re-render of the component previous variables will get lost. So React provided a ref way of persisting data across re-render inside the components using Hooks.

And then inside the handleInputChange handler, we're calling the function stored inside the inputRef.current variable.

const handleInputChange = (event) => { const input = event.target.value; setInput(input); inputRef.current(input);};

If you run the application now, you will see the expected behavior similar to debouncing inside class.

Hooks With

That's it about this article. I hope you enjoyed the article and found it useful.

You can find the complete source code for this application in this repository and live demo at this url

Don't forget to subscribe to get my weekly newsletter with amazing tips, tricks and articles directly in your inbox here.


Original Link: https://dev.to/myogeshchavan97/using-debouncing-to-improve-the-performance-of-your-application-s-search-functionality-31j7

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To