0

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..!

Evan Jin
  • 123
  • 6

3 Answers3

1

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.

Evan Jin
  • 123
  • 6
0

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)}
      onMouseLeave={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") )
Kuncheria
  • 1,182
  • 9
  • 16
  • Oh my question detail was not enough and my bad! actually I wanna keep 'wait cursor' for 500ms when I move cursor out – Evan Jin Oct 03 '21 at 07:29
0

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, onMouseEnter, onMouseLeave }) => {
  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)}
          onMouseLeave={(e) => setHoverItem(null)}
        />
      ))}
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
Long Nguyen Duc
  • 1,317
  • 1
  • 8
  • 13
  • 1
    I tried this before, and if you add 'console.log' in Item Component, 12 items will re-render everytime each component get hovered. and finally found problem and left the solution below your answer! btw really thanks for your efforts! – Evan Jin Oct 03 '21 at 09:36