I recently added a feature to the admin interface on Meteor Galaxy, Meteor Development Group’s hosting platform for Meteor apps, that shows the state of our internal database migrations. I’m using Apollo Client with react-apollo to build Galaxy’s UI, and I wanted the control panel to automatically update to show the state of any migrations. To do that, I had to decide how often the GraphQL query would poll our server.

Now, we’re not usually running any migrations, so a nice, slow polling interval like 30 seconds seemed reasonable. But in the rare case where a migration is running, I wanted to be able to see much faster updates on its progress.
Setting the poll interval from the result of a query
It turns out there’s an easy pattern with Apollo Client and react-apollo to let the poll interval be determined by the data returned by the query itself. I had to figure it out for myself, so I thought I’d share it here to save others the work! I think it’s easier to understand if I use some utilities from the great recompose
library, but you can implement this with the basic React API as well.
The key to this is knowing that the options
parameter to react-apollo’s main graphql
function can itself be a function that depends on its incoming React props. (The options
parameter describes the options for the query itself, as opposed to React-specific details like what property name to use for data.) We can then use recompose
‘s withState
to set the poll interval from a prop passed in to the graphql
component, and use the componentWillReceiveProps
React lifecycle event (added via the recompose
lifecycle
helper) to look at the fetched GraphQL data and adjust if necessary.
Here’s how it all fits together:
import { graphql } from "react-apollo";
import gql from "graphql-tag";
import { compose, withState, lifecycle } from "recompose";
const DEFAULT_INTERVAL = 30 * 1000;
const ACTIVE_INTERVAL = 500;
const withData = compose(
// Pass down two props to the nested component: `pollInterval`,
// which defaults to our normal slow poll, and `setPollInterval`,
// which lets the nested components modify `pollInterval`.
withState("pollInterval", "setPollInterval", DEFAULT_INTERVAL),
graphql(
gql`
query getMigrationStatus {
activeMigration {
name
version
progress
}
}
`,
{
// If you think it's clear enough, you can abbreviate this as:
// options: ({pollInterval}) => ({pollInterval}),
options: props => {
return {
pollInterval: props.pollInterval
};
}
}
),
lifecycle({
componentWillReceiveProps({
data: { loading, activeMigration },
pollInterval,
setPollInterval
}) {
if (loading) {
return;
}
if (activeMigration && pollInterval !== ACTIVE_INTERVAL) {
setPollInterval(ACTIVE_INTERVAL);
} else if (
!activeMigration &&
pollInterval !== DEFAULT_INTERVAL
) {
setPollInterval(DEFAULT_INTERVAL);
}
}
})
);
const MigrationPanelWithData = withData(MigrationPanel);
Note that we check the current value of pollInterval
before changing it, because by default in React, nested components will get re-rendered any time we change state, even if you change it to the same value. You can deal with this using componentShouldUpdate
or React.PureComponent
, but in this case it’s straightforward just to only set the state when it’s actually changing.
Using this pattern successfully requires at least version 2.0.3 of apollo-client
, as earlier versions had a bug related to changing pollInterval
.
I hope others find this pattern as useful as I did! This sort of feature is why I find Apollo to be the most delightful way to get data into my apps, even independently of the advantages of GraphQL itself.
Stay in our orbit!
Become an Apollo insider and get first access to new features, best practices, and community events. Oh, and no junk mail. Ever.
Make this article better!
Was this post helpful? Have suggestions? Consider so we can improve it for future readers ✨.