Reputation: 1471
How can I clean up function like setTimeout
or setInterval
in event handler in React? Or is this unnecessary to do so?
import React from 'react'
function App(){
return (
<button onClick={() => {
setTimeout(() => {
console.log('you have clicked me')
//How to clean this up?
}, 500)
}}>Click me</button>
)
}
export default App
Upvotes: 3
Views: 2397
Reputation: 572
Clear timer when unmount component
import React from 'react'
function App(){
const timerRef = React.useRef(null)
React.useEffect(() => {
return () => {
// clean
timerRef.target && clearTimeout(timerRef.target)
}
},[])
return (
<button onClick={() => {
timerRef.target = setTimeout(() => {
console.log('you have clicked me')
}, 500)
}}>Click me</button>
)
}
export default App
Upvotes: -1
Reputation: 1597
One more solution (Live Demo):
import React, { useState } from "react";
import { useAsyncCallback } from "use-async-effect2";
import { CPromise } from "c-promise2";
export default function TestComponent(props) {
const [text, setText] = useState("");
const click = useAsyncCallback(function* (ms) {
yield CPromise.delay(ms);
setText("done!" + new Date().toLocaleTimeString());
}, []);
return (
<div className="component">
<div className="caption">useAsyncEffect demo:</div>
<div>{text}</div>
<button onClick={() => click(2000)}>Click me!</button>
<button onClick={click.cancel}>Cancel scheduled task</button>
</div>
);
}
In case if you want to cancel the previous pending task (Live demo):
import React, { useState } from "react";
import { useAsyncCallback } from "use-async-effect2";
import { CPromise } from "c-promise2";
export default function TestComponent(props) {
const [text, setText] = useState("");
const click = useAsyncCallback(
function* (ms) {
console.log("click");
yield CPromise.delay(ms);
setText("done!" + new Date().toLocaleTimeString());
},
{ deps: [], cancelPrevios: true }
);
return (
<div className="component">
<div className="caption">useAsyncEffect demo:</div>
<div>{text}</div>
<button onClick={() => click(5000)}>Click me!</button>
<button onClick={click.cancel}>Cancel scheduled task</button>
</div>
);
}
Upvotes: -1
Reputation: 1075317
Whether it's necessary depends on what the callback does, but certainly if the component is unmounted it almost doesn't matter what it does, you do need to cancel the timer / clear the interval.
To do that in a function component like yours, you use a useEffect
cleanup function with an empty dependency array. You probably want to store the timer handle in a ref
.
(FWIW, I'd also define the function outside of the onClick
attribute, just for clarity.)
import React, {useEffect, useRef} from 'react';
function App() {
const instance = useRef({timer: 0});
useEffect(() => {
// What you return is the cleanup function
return () => {
clearTimeout(instance.current.timer);
};
}, []);
const onClick = () => {
// Clear any previous one (it's fine if it's `0`,
// `clearTimeout` won't do anything)
clearTimeout(instance.current.timer);
// Set the timeout and remember the value on the object
instance.current.timer = setTimeout(() => {
console.log('you have clicked me')
//How to clean this up?
}, 500);
};
return (
<button onClick={onClick}>Click me</button>
)
}
export default App;
An object you store as a ref is usually a useful place to put things you would otherwise have put on this
in a class component.
(If you want to avoid re-rendering button
when other state in your component changes (right now there's no other state, so no need), you could use useCallback
for onClick
so button
always sees the same function.)
Upvotes: 3