Hack for your particular case
Assuming you're receiving id as props or some such, would this work?
const Foo = ({ id }) => {
const [data, setData] = useState()
const passedId = useRef()
passedId.current = id
useInterval(() => {
fetchData(id)
.then(response => id === passedId.current && setData(response))
}, 5000)
// Render some JSX with the data
I tested something very similar to this locally and essentially what happens is this:
Foo receives id = 6
6 is stored in passedId
useInterval ticks and we request id = 6
- the component receives
id = 7
7 is stored in passedId
- the request for
id = 6 completes, but passedId.current === 7 so setData isn't called
- the request for
id = 7 completes, id === passedId.current and setData is called
Getting something like this into useInterval
Note - not tested
Maybe the reason why cancelling an effect is so easy is because the function returns its own cleanup so ignore doesn't have to be scoped externally. But setInterval doesn't allow return values. We might be able to get around this using a setTimeout instead:
function useInterval(callback, delay) {
useEffect(() => {
let cleanup
let id
function tick() {
cleanup = callback()
id = setTimeout(tick, delay)
}
if (delay !== null) {
tick()
return () => {
clearTimeout(id)
cleanup && cleanup()
}
}
return () => cleanup && cleanup()
}, [callback, delay])
One catch with this is that now we have callback in the dependency array so the callback given to useInterval should be created using useCallback:
const Foo = ({ id }) => {
const [data, setData] = useState()
const pollData = useCallback(() => {
let shouldUpdate = true
fetchData(id).then(resp => shouldUpdate && setData(resp))
return () => shouldUpdate = false
}, [id])
useInterval(pollData, 5000)
// Render some JSX with the data
- When
useInterval is called, we setup a useEffect that executes callback
- When
callback executes, the return value (the cleanup for it) is stored in cleanup which is scoped to the effect and a setTimeout is setup to re-execute in delay milliseconds
- If
callback changes (i.e. we get a new id), then we clear the timeout and run the cleanup for the previous callback
- After
delay milliseconds, tick is executed again