Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

react hooks setTimeout after setState

I recently wanted to design an input component with react hooks. The component would check validation after entering input in 0.5 second.

my code like

const inputField = ({ name, type, hint, inputValue, setInput }) => {

    // if there is default value, set default value to state
    const [value, setValue] = useState(inputValue);

    // all of validation are true for testing
    const validCheck = () => true;

   let timeout;

    const handleChange = e => {
        clearTimeout(timeout);
        const v = e.target.value;

        setValue(v);

        timeout = setTimeout(() => {
            // if valid
            if (validCheck()) {
               // do something...
            }
        }, 500);
    };

    return (
        <SCinputField>
            <input type={type} onChange={handleChange} />
       </SCinputField>
    );
};

unfortunately, it's not worked, because the timeout variable would renew every time after setValue.

I found react-hooks provide some feature like useRef to store variable.

Should I use it or shouldn't use react-hooks in this case?

Update

add useEffect

const inputField = ({ name, type, hint, inputValue, setInput }) => {

    // if there is default value, set default value to state
    const [value, setValue] = useState(inputValue);

    // all of validation are true for testing
    const validCheck = () => true;

   let timeout;

    const handleChange = e => {
        const v = e.target.value;

        setValue(v);
    };

    // handle timeout
    useEffect(() => {
        let timeout;

        if (inputValue !== value) {
            timeout = setTimeout(() => {
                const valid = validCheck(value);
                console.log('fire after a moment');

                setInput({
                    key: name,
                    valid,
                    value
                });
            }, 1000);
        }

        return () => {
            clearTimeout(timeout);
        };
    });


    return (
        <SCinputField>
            <input type={type} onChange={handleChange} />
       </SCinputField>
    );
};

It looks worked, but I am not sure about it's a right way to use.

like image 255
PaulHuang Avatar asked Nov 27 '22 19:11

PaulHuang


1 Answers

Here's how I would do it:

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

function InputField() {

  const [value, setValue] = useState('');       // STATE FOR THE INPUT VALUE
  const timeoutRef = useRef(null);              // REF TO KEEP TRACK OF THE TIMEOUT

  function validate() {                         // VALIDATE FUNCTION
    console.log('Validating after 500ms...');
  }

  useEffect(() => {                             // EFFECT TO RUN AFTER CHANGE IN VALUE
    if (timeoutRef.current !== null) {          // IF THERE'S A RUNNING TIMEOUT
      clearTimeout(timeoutRef.current);         // THEN, CANCEL IT
    }

    timeoutRef.current = setTimeout(()=> {      // SET A TIMEOUT
      timeoutRef.current = null;                // RESET REF TO NULL WHEN IT RUNS
      value !== '' ? validate() : null;         // VALIDATE ANY NON-EMPTY VALUE
    },500);                                     // AFTER 500ms
  },[value]);                                   // RUN EFFECT AFTER CHANGE IN VALUE

  return(                                       // SIMPLE TEXT INPUT
    <input type='text' 
      value={value} 
      onChange={(e) => setValue(e.target.value)}
    />
  );

}

WORKING EXAMPLE ON SNIPPET BELOW:

function InputField() {

  const [value, setValue] = React.useState('');
  const timeoutRef = React.useRef(null);

  function validate() {
    console.log('Validating after 500ms...');
  }

  React.useEffect(() => {
    if (timeoutRef.current !== null) {
      clearTimeout(timeoutRef.current);
    }
     
    timeoutRef.current = setTimeout(()=> {
      timeoutRef.current = null;
      value !== '' ? validate() : null;
    },500);
  },[value]);
  
  return(
    <input type='text' value={value} onChange={(e) => setValue(e.target.value)}/>
  );
  
}

ReactDOM.render(<InputField/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>

<div id="root"/>
like image 103
cbdeveloper Avatar answered Dec 04 '22 07:12

cbdeveloper