Home > Blockchain >  React canceling clearTimeout not working properly on useEffect
React canceling clearTimeout not working properly on useEffect

Time:10-04

https://codepen.io/evan-jin/pen/qBjGWvR

  const [hoverItem, setHoverItem] = useState(null)
  const timerRef = useRef(null)
  
  const addcursor = useCallback(() => {
    console.log('addcursor')
    clearTimeout(timerRef.current)
    timerRef.current = null
    
    timerRef.current = setTimeout(() => {
      document.body.style.cursor = 'wait'
    }, 10)
  }, [])
  
  const removeCursor = useCallback(() => {
    if (timerRef.current === null) return
    console.log('removeCursor')
    
    timerRef.current = setTimeout(() => {
      document.body.style.cursor = 'default'
    }, 500)
  }, [])
  
  useEffect(() => {
    if (hoverItem) {
      addcursor()
    } else {
      removeCursor()
    }
  }, [hoverItem])

I wanna keep 'wait cursor' whenver hover on box div but setTimeout triggers when I stop moving cursor.

I tried to use clearTimeout on useEffect return(willUnmount) but doesn't work.

It works if I move in and out on each box component but If I move Cursor fast to the other one, It ends up triggering RemoveCursor setTimeout at the end.

To sum up,

  1. I wanna keep 'wait cursor' for 500ms when I move cursor out
  2. After pass through boxs, 'wait cursor' need to stay on box if cursor is on it but not just disappearing

this question looks weird but I just made this simple problem for my real project please anybody help me..!

CodePudding user response:

I finally found the problem! since I declared timerRef in each Item Component. So each timerRef referred each Item timer. And I could simply solve this problem by declaring timerRef in parent component! I will leave the code on that Link. thanks for you guys efforts.

CodePudding user response:

It looks like you are using timeout un necessarily. You can achieve what you are trying to do without the setTimeout.

const { useState, useEffect, useCallback, useRef } = React

const Item = ({ num }) => {
    const [hoverItem, setHoverItem] = useState(null)
  
  const addCaption = useCallback(() => {
    console.log('addCaption')
    document.body.style.cursor = 'wait'
  }, [])
  
  const removeCaption = useCallback(() => {
    console.log('removeCaption')
   
    document.body.style.cursor = 'grab'
  }, [])
  
  useEffect(() => {
    if (hoverItem) {
      addCaption()
    } else {
      removeCaption()
    }
  }, [hoverItem])
  
  return (
    <div 
      className='square'
      name={num}
      id={num}
      onMouseEnter={e => setHoverItem(e.target.id)}
      onm ouseLeave={e => setHoverItem(null)}
    />
  )
}

const App = () => {
  
  return (
    <div className='wrapper'>
      {[...Array(12)].map((_, i) => (
        <Item key={i} num={i} />
      ))}
    </div>
  )
}

ReactDOM.render( <App/>, document.getElementById("root") )

CodePudding user response:

Because you present a state for each Item, but you only need a single state (standing for "the cursor is inside any Item or not"). Just move up your state to parent Component your code will work.

const { useState, useEffect, useCallback, useRef } = React;

const Item = ({ num, onm ouseEnter, onm ouseLeave }) => {
  return (
    <div
      className="square"
      name={num}
      id={num}
      onMouseEnter={onMouseEnter}
      onMouseLeave={onMouseLeave}
    />
  );
};

const App = () => {
  const [hoverItem, setHoverItem] = useState(null);
  const timerRef = useRef(null);

  const addcursor = useCallback(() => {
    console.log("addcursor");
    document.body.style.cursor = "wait";
    clearTimeout(timerRef.current);
  }, []);

  const removeCursor = useCallback(() => {
    console.log("removecursor");
    clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => {
      document.body.style.cursor = "default";
    }, 500);
  }, []);

  useEffect(() => {
    if (hoverItem) {
      addcursor();
    } else {
      removeCursor();
    }
  }, [hoverItem]);

  return (
    <div className="wrapper">
      {[...Array(12)].map((_, i) => (
        <Item
          key={i}
          num={i}
          onMouseEnter={(e) => setHoverItem(e.target.id)}
          onm ouseLeave={(e) => setHoverItem(null)}
        />
      ))}
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
  • Related