[React] Wait for User to stop typing then execute a function

June 21, 2021

programming
article cover
Image by ConvertKit

Problem

When you need to implement a real-time search form, you can just do that by firing a specific function on the onChange event. But if you are to call API, that’s not really good practice because you end up calling API so many times unnecessarily.

api is called everytime you put a character in input form

Solution

To avoid that problem, we better execute a function in proper timing which means after a user stops typing for a while. And setTimeout helps us to do that.

setTimeout(function, milliseconds, param1, param2, …)

The setTimeout() method calls a function or evaluates an expression after a specified number of milliseconds.

The way it works is that a timer is set to execute the function after xx seconds (let’s say 0.5 seconds) each time the user types a character, and when the next character is typed, the previously set timer is canceled and a new timer is set. In this way, the function is only executed 0.5 seconds after the end of the user’s continuous input.

visual explanation

Code

1. With useEffect

import React, { useState, useEffect } from 'react'

const fakeApi = () => console.log('Api is called')

export default function App() {
  const [inputValue, setInputValue] = useState('')

  useEffect(() => {
    const timer = setTimeout(() => {
      fakeApi()
    }, 500)

    return () => clearTimeout(timer)
  }, [inputValue])

  return (
    <div>
      <label>Input: </label>
      <input
        value={inputValue}
        type="text"
        onChange={e => setInputValue(e.target.value)}
      />
    </div>
  )
}

useEffect works each time the variables are updated specified in the second argument. Using this, keep resetting a timer.

2. Without useEffect

import React, { useState, useEffect } from 'react'

const fakeApi = () => console.log('Api is called')

export default function App() {
  const [inputValue, setInputValue] = useState('')
  const [timer, setTimer] = useState(null)

  const inputChanged = e => {
    setInputValue(e.target.value)

    clearTimeout(timer)

    const newTimer = setTimeout(() => {
      fakeApi()
    }, 500)

    setTimer(newTimer)
  }
  
  return (
    <div>
      <label>Input: </label>
      <input value={inputValue} type="text" onChange={inputChanged} />
    </div>
  )
}

Without useEffect is a bit more straightforward. And it doesn’t fire unnecessarily on the first render.

a function is executed only after user stops typing

Profile picture

Photographer / Web engineer working in Berlin.
A Japanese living with a Slovak wife and two German cats.