Robert Templeton
Robert Templeton

Reputation: 149

React Hook delayed useEffect firing?

I am trying to use useEffect() in my react hook to update the state when my props change. but there is a delay and the useEffect is only firing after I click again on an element in my hook. Im fairly new to using hooks and any help would be appreciated. Thanks

function ImageOrderSelect (props) {
  const [clicked, setClicked] = useState(false)
  const [options, setOptions] = useState(props.options)
  const [current, setCurrent] = useState(props.current)

  useEffect(() => {
      setCurrent(props.current)
    }, [props.current])


  if(!clicked){
    return (
        <div className="image-order-select-icon" onClick={() => setClicked(!clicked)}>
            <FontAwesomeIcon size="lg" icon={faCircle} />
            <p>{current}</p>
        </div>
    )
  } else if(clicked){
      return (
          <div className="image-order-select">
              {optionsList}
          </div>
      )
  }


}

Upvotes: 11

Views: 48165

Answers (5)

Andrey Butikov
Andrey Butikov

Reputation: 1

const timeout = useRef<NodeJS.Timeout>();
  useEffect(() => {
    clearTimeout(timeout.current);
    timeout.current = setTimeout(() => {
      "Your action here"
    });
  }, [your dependencies here]);

Upvotes: 0

Arturo Hernandez
Arturo Hernandez

Reputation: 2869

If the component is unloaded by a user action like moving to another window. The timeout examples here will still execute the action. Depending on the case it would produce bugs. Here is an example of how to remove the timer on unload.

  const [timerReference, setTimerReference] = useState(undefined);

  useEffect(() => {
    setTimerReference(setTimeout(() => {
      handler();
    },2000));
    return () => {
      clearTimeout(timerReference);
    }
  }, []);

Upvotes: 1

Abu Dujana Mahalail
Abu Dujana Mahalail

Reputation: 1911

    useEffect(() => {

      setTimeout(()=>{
       setCurrent(props.current)
      }, 1000)
  
    }, [props.current])

You just need to add timeout/delay before displaying the current...

or Best approach add a callback function

useEffect(() => {

 
   setCurrent(() => props.current)
  

}, [props.current])

Upvotes: 13

Jan-Philipp Marks
Jan-Philipp Marks

Reputation: 1539

Im not exactly sure what desired effect you are going for, but here is a little cleaned up code of yours. Maybe that will help you further.

function ImageOrderSelect ({ current, options ) { // deconstruct here to save code
  const [clicked, setClicked] = useState(false);
  // you dont have to keep additional state since props is a state.
  useEffect(() => {
    setTimeout(() => {
      // do something here 1 sec after current has changed
    }, 1000);
  }, [current]);

  useEffect(() => {
    setTimeout(() => {
       // do something 1 sec after clicked has changed
    }, 1000);
  }, [clicked]);

  if(!clicked){
    return (
        <div className="image-order-select-icon" onClick={() => setClicked(!clicked)}>
            <FontAwesomeIcon size="lg" icon={faCircle} />
            <p>{current}</p>
        </div>
    )
  } else if(clicked){
      return (
          <div className="image-order-select">
              {optionsList}
          </div>
      )
  }
}

In case you want an effect triggered 1sec after the last click on your image, you have to reset the timeout on every click untill it expires. This is common practice for any delayed effect on user interactions:

let timeout;

useEffect(() => {
  clearTimeout(timeout);
  timeout = setTimeout(() => {
     // do something 1 sec after a click is done
     // but dont do anything if another click happened before 1sec of the previous 
     // click has expired
  }, 1000);
}, [clicked]);

I can definetlely help you if you could give a more detailed code example and more in depth explanation of your desired UI effect.

Upvotes: 3

Robert Templeton
Robert Templeton

Reputation: 149

  let optionsList = options.map((option, index) => {
  return (
  <div key={index} onClick={() => {

      props.handleImageSort(option, props.index)
      setTimeout(() => {
          setClicked(!clicked)
      }, .500)
  }}>
    <p>{option}</p>
  </div>
  )

})

The issue was I was trying to call setClicked at the same time which for some reason was stopping it from calling. the setTimeout makes it work although not ideal. if anyone has a better solution would be interested to hear it.

Upvotes: 0

Related Questions