'Can someone please tell me what am I doing wrong here? [React hooks]

I am trying to use the useDebounce hook in the handleChange function. But I am getting the following error

React Hook "useDebounce" is called in function "handleChange" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. (react-hooks/rules-of-hooks)

The code is as follows

import "./styles.css";
import { useEffect, useState } from 'react'

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value)

  useEffect(() => {
    const timer = setTimeout(() => setDebouncedValue(value), delay || 500)

    return () => {
      clearTimeout(timer)
    }
  }, [value, delay])

  return debouncedValue
}

const handleChange = (e) => {
  let debouncedValue = useDebounce(e.target.value, 500);
  console.log('debouncedValue', debouncedValue);
}

export default function App() {
  
  return (
    <div className="App">
      <input type="text" placeholder="Search" onChange={(e) => {handleChange(e)}} />
    </div>
  );
}

Request if someone can shed some light as to what am I doing wrong here.



Solution 1:[1]

Issue

Like the error states, it's invalid to call React hooks in callbacks.

See Rules of Hooks

  • Don’t call Hooks inside loops, conditions, or nested functions.
  • Don’t call Hooks from regular JavaScript functions.

Solution

It seems like you are really wanting to debounce the handleChange callback handler.

Import a debouncing utility or create your own simple debounce higher order function.

Example:

const debounce = (fn, delay) => {
  let timerId;
  return (...args) => {
    clearTimeout(timerId);
    timerId = setTimeout(() => fn(...args), delay);
  }
};

Usage:

export default function App() {
  const [value, setValue] = useState(value);

  const handleChange = e => {
    setValue(e.target.value);
  }

  const debouncedHandler = debounce(handleChange, 500);
  
  return (
    <div className="App">
      <input type="text" placeholder="Search" onChange={debouncedHandler} />
    </div>
  );
}

Solution 2:[2]

Hooks in React can only be called from React components or from other hooks.
React seems to validate hooks/components via their name scheme.
I.E. - hooks start with "use", components start with an uppercase letter.

Check out React's rules of hooks section regarding this.

So what's heppening here is that you are trying to use your custom useDebounce hook in a regular function.
Which would be the equivalent of trying to use useState or useEffect outside of a React component, and will produce the same error.

The solution is to either change the naming schemes or the structure of the code.

Solution 3:[3]

Try like this

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

function useDebounce(value, delay) {
    const [debouncedValue, setDebouncedValue] = useState(value);

    useEffect(() => {
        const timer = setTimeout(() => setDebouncedValue(value), delay || 500);

        return () => {
            clearTimeout(timer);
        };
    }, [value, delay]);

    return debouncedValue;
}

export default function App() {
    const [term, setTerm] = React.useState('');
    const debouncedValue = useDebounce(term, 500);
    const handleChange = (e) => {
        setTerm(e.target.value);
        console.log('debouncedValue', debouncedValue);
    };

    return (
        <div className="App">
            <input
                type="text"
                placeholder="Search"
                onChange={(e) => {
                    handleChange(e);
                }}
            />
            {debouncedValue}
        </div>
    );
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Drew Reese
Solution 2 tomleb
Solution 3 Hakob Sargsyan