I'm working on a project at the moment where I need to be able to poll an API periodically and I'm building the application using React. I hadn't had a chance to play with React Hooks yet so I took this as an opportunity to learn a bit about them and see how to solve something that I would normally have done with class-based components and state, but do it with Hooks.
When I was getting started I kept hitting problems as either the Hook wasn't updating state, or it was being overly aggressive in setting up timers, to the point where I'd have dozens running at the same time.
After doing some research I came across a post by Dan Abramov on how to implement a Hook to work with
setInterval. Dan does a great job of explaining the approach that needs to be taken and the reasons for particular approaches, so go ahead and read it before continuing on in my post as I won't do it justice.
Initially, I started using this Hook from Dan as it did what I needed to do, unfortunately, I found that the API I was hitting had an inconsistence in response time, which resulted in an explosion of concurrent requests, and I was thrashing the server, not a good idea! But this was to be expected using
setInterval, it doesn't wait until the last response is completed before starting another interval timer. Instead I should be using
setTimeout in a recursive way, like so:
In this example the console is written to approximately once every second, but if for some reason it took longer than basically instantly to write to the console (say, you had a breakpoint) a new timer isn't started, meaning there'll only ever be one pending invocation.
This is a much better way to do polling than using
setTimeout with React Hooks
With React I've created a custom hook like Dan's
The way this works is that the
tick function will invoke the
callback provided (which is the function to recursively call) and then schedule it with
setTimeout. Once the callback completes the return value is checked to see if it is a
Promise, and if it is, wait for the
Promise to complete before scheduling the next iteration, otherwise it'll schedule it. This means that it can be used in both a synchronous and asynchronous manner:
Here's a demo:
setTimeout is useful for you, feel free to copy the code or put it on