Navigate back to the homepage
BLOG
Try NowLogin
Back

Polling in React using the useInterval Custom Hook

Paige Niedringhaus
October 9th, 2020 · 6 min read

Introduction

The web application my development team is building at work provides a lot of data to our users, including downloadable Excel spreadsheets that can sometimes contain up to 250,000 rows’ worth of information. And our users have the ability to generate these reports, on demand, from our tool.

As you may imagine, it can sometimes take several minutes or more to compile this much data, and waiting around looking at a loading animation while our report service crunches numbers in the background wouldn’t be a great user experience.

Instead, we needed a way to check when the report service was done generating a report, without blocking a user from doing other work in our application while that was going on. We needed polling.

Sometimes when building web applications, you’re going to need to keep track of data being created or updated asynchronously by another service. Polling, is one way to accomplish this, and all it is, is simply hitting an API’s endpoint to retrieve any new data at a set interval of time.

This is exactly the type of solution my team needed to implement — a way to periodically keep checking if the reports (being generated by our separate report service) were done, so they could be displayed in the UI for our users to download once they were. And we needed a solution that would work for React.

Today, I’ll be sharing how easy it is to leverage a custom React Hook called useInterval, to poll an API within your application for fresh data (or really run any task that needs intervals).

Polling VS Webhooks

An alternative to polling is webhooks. Webhooks are a common way for one app to notify of an event, in real-time, to another app.

Webhooks are often referred to as “reverse APIs” because the app consuming the data does not do so by sending an HTTP request to another API — instead, it makes an API available for the app sending the data (usually, a POST HTTP request).

Example: Getting notified when a new component is available

Let’s say one of your teammates has published a component to your shared component collection. You might want to use it in your current project but how would you know that it’s now available?

One way would be by polling.

Another, more elegant and efficient way, would be by using webhooks.


Why am I telling you about webhooks in a post about polling? Well, it’s something to keep in mind when you’re building a solution for a problem that’s potentially solved by both techniques. It may very well be that you don’t actually have an option and polling is the only available solution but if that’s not the case, you should at least consider webhooks.

Intervals from React Classes to React Hooks

Before I dive into the custom useInterval Hook, let me briefly review how polling at intervals used to work in React when we could only use class-based components to work with our state.

SetInterval: The Old Way to Poll in React Classes

Back in the days when we used to be restricted to class-based components for state changes, polling was typically started on the componentDidMount() lifecycle method as soon as the component was mounted in the DOM. And as long as the component stayed mounted, the setInterval() function would continue indefinitely. Upon unmounting the component with componentWillUnmount(), the interval will be cleared and stop running with clearInterval(). Here’s an example of that code below.

PollingSample.js

how-polling-intervals-used-to-work-in-React A simple example of how polling intervals used to work in React.

In the example above, I just have the PollingExample’s pollingCount state updating every 3 seconds, as determined by the delay, which is currently set to 3000ms.

When the component mounts, the setInterval() starts running, and every 3 seconds it increments the pollingCount’s state by 1 by calling the tick() function, which actually updates the state. Once the PollingExample component unmounts from the DOM, the clearInterval() function is called and the interval stops running.

This works fine, but if multiple components need intervals to poll for different data or execute other tasks on an interval, there can be a lot of redundant, interval-related code scattered among the components.

Setting up a running interval is pretty simple, but now that we’ve got React Hooks there’s an even simpler, cleaner way to encapsulate polling data into a custom Hook.

UseInterval: The New Way to Poll with React Hooks

When React Hooks first came out, people were critical that setInterval() didn’t work with Hooks the way that it had with React’s class-based components.

And it’s true, it doesn’t. Dan Abramov wrote a very in-depth explanation of why this is the case (and does a fantastic job of explaining it, so I’ll continue to leave that to him). But in addition to demystifying how setInterval() works with React’s lifecycles like componentDidMount(), he also introduced the new Hooks-way of correctly writing intervals to work in function-based React components.

He introduced a new custom hook called useInterval, and it’s awesome, easy to use, and just what my team needed. Here’s the code for the Hook, taken just as Dan wrote it, and added right to my team’s project in our hooks/ folder.

The useInterval Hook

useInterval.js

custom-interval-hook The code for the custom useInterval Hook, just as Dan Abramov wrote it.

This useInterval Hook sets up an interval and clears it after the component unmounts. It’s a combo of setInterval() and clearInterval(), but all wrapped into one easy-to-implement Hook.

All you need to do to get the Hook to work is supply it a function (the callback parameter) and an interval time (the delay). Simple!

With this Hook, more than half the battle is already over, now you’re ready to drop this interval Hook wherever you need it in your code. For me and my team, that happened to be in the reports page.

Putting useInterval to Use

ReportsPage.js

Here’s a condensed version of my ReportsPage functional component with the useInterval Hook imported at the top of the component and dropped into the code.

useInterval-hook-encapsulates-interval-logic Instead of managing multiple component lifecycles, the useInterval hook encapsulates all the interval logic into itself, making it easy to reuse in many places in the code.

In my component, the useInterval Hook takes in an asynchronous call to fetch any reports that have been generated the by the report service (reportService.getGeneratedReports()), and then it updates the reports available for download in the UI with setReports().

For ease-of-updating the interval timing if need be, I’m using a constant called REPORT_REFRESH_INTERVAL, which is set to run this polling interval every 15 seconds while the component is mounted.

I added the console.log so it’s easier to tell if the interval is running correctly.

confirms-running-on-an-interval Inelegant? Maybe. Confirms it’s running on an interval? Sure does.

The count of 11 ahead of the logged message "Checking if reports are ready to download" demonstrates that the useInterval Hook is firing at regular intervals while users are on the Report page. If you checked the Network panel right now in the Developer Tools, you’d also see 11 individual API calls to the reportService’s getGeneratedReports() REST endpoint.

Once the user navigates to a different page or component, you’ll see the API calls cease with no extra code on my part. Only if the user returns to the Reports page will the polling for reports resume.

How simple is that? Instead of having to write multiple instances of setInterval(), clearInterval() inside of your class-based components’ lifecycles when you need intervals, you can have one useInterval Hook that can drop into any functional component anywhere in the app that you need polling.

Now that’s how you poll efficiently with a custom React Hook. And you could use this very same Hook anywhere in your code that an interval is needed.


Conclusion

Sometimes you need a simple way to check if an asynchronous task being performed by a separate service is done or not. Polling is one way to do this; hitting an endpoint over and over at some set interval to check for fresh data to display in the UI.

With the introduction of React Hooks, and especially the ability to put together custom Hooks, creating a reusable Hook called useInterval to serve just such a purpose seemed inevitable. Dan Abramov, part of the React core team, obliged, and the real beauty of it is, this Hook is highly reusable. It can be dropped anywhere in the app, to run any sort of task, at any interval with next to no effort. That’s the kind of solution I like.

Check back in a few weeks — I’ll be writing more about JavaScript, React, ES6, or something else related to web development.

Thanks for reading. If you need intervals in your React app and you’re using Hooks, consider adding this useInterval Hook to your arsenal, it’s a cinch to use and easy to customize to your specific needs.

Read the original article or more interesting posts on Paige’s blog.

If you enjoyed reading this, you may also enjoy some of my other free pieces:


References and Further Resources

Frontend Monitoring

Asayer is a frontend monitoring tool that replays everything your users do and shows how your web app behaves for every issue. It lets you reproduce issues, aggregate JS errors and monitor your web app’s performance.

Happy debugging, for modern frontend teams - Start monitoring your web app for free.

More articles from Asayer Blog

Developing React JS Global State Library With Atom Abstraction

In line with Recoil, Atom Abstraction eliminates selector functions for render optimization and makes API pretty intuitive.

October 5th, 2020 · 3 min read

Redux In Web Workers: Off-Main-Thread Redux Reducers and Middleware

Several experiments to off load Redux, often used with React, from the main thread, and run some or all of Redux store in Web Workers

October 2nd, 2020 · 2 min read
© 2020 Asayer Blog
Link to $https://twitter.com/asayerioLink to $https://github.com/asayerioLink to $https://www.linkedin.com/company/18257552