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,
- I wanna keep 'wait cursor' for 500ms when I move cursor out
- 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"));