From React Class component to Functional component with hooks

Improve the performance with the React UseEffect() hook and debouncing

In the previous episodes we have build a controlled input and a controlled form. We also have discovered that the React useEffect hook could help us to improve performance consuming less memory.

In this article, we will learn what is debouncing and how to use it with useEffect. We will improve the performance of our previous controlled input with the use the useEffect hook and a manually written debounce function.

Summary

Caution : the useEffect hook must be used inside functional components only!

Remark: As usual, before starting coding, just check that React, React.Dom libraries and Babel preprocessor have been installed.

Review: What is the React useEffect() hook?

The useEffect() hook is used to execute (effect) an action at a specific moment of the lifecycle of the components. The triggers are listed in an array of dependencies.

Using useEffect, React guarantees the DOM has been updated by the time it runs the effects.

What is debouncing?

In JavaScript, two similar functions, Debounce and Throttle, allow us to control and so reduce the number of times a function is executed during a specific interval of time.

These functions are very useful when resizing the window, keyboard typing, scrolling or avoiding too many clicks on the same action button!

In this article, we will study the debounce function.

The debounce function definition

The debounce function allow us to call a method/a function only once at the beginning or the end of a series of executions of the same action.

The debounce function operation

In fact, the debounce function use the setTimeOut() method to apply a delay before executing the action. If the user asks the action to be done once again during this delay, the timer is reset and… the delay before executing the action will also be reinitialized. And so on…

The debounce function examples of use

Imagine you have to type an URL in an input to fetch data… without debouncing a call to the API is made every time you type a letter and an error is sent back every time until the URL is fully completed!

Imagine you have to type a search in an input… without debouncing a search is launched every time you type a letter!

Imagine you have to type a pin code in an input… without debouncing a login is launched every time you type a letter!

There are so many other examples…

A React controlled input performance improved with useEffect and debouncing

Do you remember the controlled input we built in a previous article? We are going to use it as a basis. First, we will add a useEffect hook and then a debounce function. Finally, we will write a more generic debounce function we will be able to slightly modify to adapt it to search or fetch data.

Step 1 – A basic controlled input with React – Review

The code of a basic controlled input

const { useState } = React;
const ControlledInput = () => {
    const [input, setInput] = useState(”);
    return (
        <div>
            <input
                value={input}
                onChange={(e)=> setInput(e.target.value)} />
            <h4>Controlled input</h4>
            <p>{input}</p>
        </div>
    )
}
ReactDOM.render(<ControlledInput />, document.getElementById(‘input’));

The structure of a basic controlled input

  • The state of the input value is initialized with an empty string passed as an argument to the useState function.
  • The input state is updated through the setInput function whose passed argument is the event.target.value, every time a character is typed in the input field by the user.
  • The input value is displayed below the input.
  • As a result, every time a letter is typed, the state is updated and… the displayed text is updated too!

Play with the basic controlled input

See the Pen
React tricks: Create a controlled input with the hooks useState
by Coding-Tricks (@coding-tricks)
on CodePen.

Step 2 – A basic controlled input with the useEffect React hook

The code of a basic input with useEffect

const { useState, useEffect } = React;
const ControlledInput = () => {
    const [input, setInput] = useState(”);
    const [displayedValue, setDisplayedValue] = useState(”);
    const handleChange = (e) => {
        setInput(e.target.value);
    };
    useEffect(() => {
        setDisplayedValue(input);
    }, [input])
    return (
        <div>
            <input value={input} onChange={handleChange} />
            <h4>Controlled input</h4>
            <p>{displayedValue}</p>
        </div>
    )
}

The structure of a basic controlled input with useEffect

  • Write the basic controlled input.
  • The state of the displayedValue is initialized with an empty string passed as an argument to the useState function.
  • The paragraph displays the displayedValue instead of the input value.
  • The useEffect hook is imported and used to update the displayed value with the setDisplayedValue function every time the input is triggered.
  • As a result, every time a letter is typed, the state is updated and… the displayed text is updated too! No visible change! But, the displayed value will not be re-rendered when another part of the component is modified.

Step 3 – A basic controlled input with useEffect and debouncing

The code of a debounced controlled input with React

const { useState, useEffect } = React;
const ControlledInput = () => {
    const [input, setInput] = useState(”);
    const [displayedValue, setDisplayedValue] = useState(”);
    const handleChange = (e) => {
        setInput(e.target.value);
    };
    useEffect(() => {
        const displayInputcontent = setTimeout(() => {
        setDisplayedValue(input)}, 2000);
        return () => clearTimeout(displayInputcontent);
    }, [input])
    return (
        <div>
            <input value={input} onChange={handleChange} />
            <h4>Controlled input</h4>
            <p>{displayedValue}</p>
        </div>
    )
}

The structure of a basic debounced controlled input with React

  • In the useEffect hook, declare a function which call the setTimeout() method.
  • Pass the setDisplayedValue function and the delay to the setTimeout() method.
  • In the useEffect hook, use the cleanup function to reset the timer with the clearTimeout() method.
  • Trick: if you forget the cleanup function the displayed value will appear letter by letter after a delay. If you do not forget to add the cleanup function, the dysplayed value will  appear at one go as “a word” after the indicated delay!
  • Conclusion: The input value is displayed at one go after an indicated delay! the performance has been improved!
  • Remark: The example is a basic controlled input which typed value is displayed. But the value could have been used to fetch data. The structure would have been the same; the setDisplayedValue function would just have been replaced by the API call.

Play with the debounced controlled input

See the Pen
React tricks: a controlled input debounced with useEffect-1
by Coding-Tricks (@coding-tricks)
on CodePen.

A more generic debounce function with React.js

Until now we have coded a specific debounce function for a specific action in a specific component because that was the easier way to explain the ins and outs.

What if you have many inputs needed many different debounce functions in the same component or in the same application? Wouldn’t it be great to code only one debounce function and call it every time we need an action to be debounced passing it specific arguments?

The code of a debounce value function

const { useState, useEffect } = React;
function useDebouncedValue(value, delay) {
    const [debouncedValue, setDebouncedValue] = useState(value);
    useEffect(() => {
        const displayContent = setTimeout(() => setDebouncedValue(value), delay);
        return () => clearTimeout(displayContent);
    }, [value]);
    return debouncedValue;
}
const ControlledInput = () => {
    const [input, setInput] = useState(”);
    const handleChange = (e) => {
        setInput(e.target.value);
    };
    const displayDebouncedInputContent = useDebouncedValue(input, 1000);
    return (
        <div>
            <input value={input} onChange={handleChange} />
            <h4>Controlled input</h4>
            <p>{displayDebouncedInputContent}</p>
        </div>
    )
}

The structure of a debounce value function

The generic debounced function

  1. Import the useState and useEffect hooks.
  2. Declare the generic useDebouncedValue function and pass it a value and a delay as arguments.
  3. In the useDebouncedValue function initialize a debouncedValue at value which is the passed argument.
  4. In the useDebouncedValue function call the useEffect hook.
  5. Inside the useEffect hook, declare a displayContent function (could be displayValue) and set value as dependency.
  6. Inside this new function, call the setTimeout() method and pass it the setDebouncedValue function with the value argument as first argument, pass it delay as the second argument.
  7. In the useEffect hook, do not forget to declare the cleanup function with the clearTimeout() method to reset the displayContent timer function.
  8. In the generic useDebouncedValue function, return the debouncedValue which is the “final state” of the modified value.
  9. Remark: this debounce function may be written in a separated file. In that case, do not forget to export it!

In the component

  1. Write the basic controlled input code.
  2. If the debounce function has been written in a separated file, import it.
  3. Declare the displayDebouncedInputContent function above the return () and call the generic debounce function passing it two arguments: input as value and a duration in milliseconds as delay.
  4. In the return(), call the just declared displayDebouncedInputContent function as displayed value in the paragraph.

Play with the debounce value function pen

See the Pen
React tricks: a controlled input debounced with a standard function
by Coding-Tricks (@coding-tricks)
on CodePen.

In conclusion

Now, we know:

In this article, we have worked on debounced displayed value. But, we could use similar structures to create a fetch data debounce function as explained by Nishant Kumar, or filter a list as explained by Dmitri Pavlutin.

Why will you not try to create a debounce function on the controlled form we built in a previous article?

You could be interested by the lodash library to avoid writing your debounce functions manually.

In the next episodes, we will see how to pass a callback as props in both class and functional components.

Haut de page