Reputation: 149
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
Reputation: 1
const timeout = useRef<NodeJS.Timeout>();
useEffect(() => {
clearTimeout(timeout.current);
timeout.current = setTimeout(() => {
"Your action here"
});
}, [your dependencies here]);
Upvotes: 0
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
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
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
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