Skip to main content

Invalidation / Refresh data

Sometimes data becomes out-of-date.

use-remote-data supports “invalidation” through an optional invalidation: InvalidationStrategy parameter to the hook.

An InvalidationStrategy is a function that decides whether to keep data or mark it stale.

By time

Here is an example where data is considered stale after 2 seconds.

In the <WithRemoteData> render prop, you can check the second argument (isInvalidated) to decide whether to let the user see old data or a loading indicator.

import * as React from 'react';
import {
InvalidationStrategy,
useRemoteData,
WithRemoteData,
} from 'use-remote-data';

var i = 0;
const freshData = (): Promise<number> =>
new Promise((resolve) => {
i += 1;
setTimeout(() => resolve(i), 1000);
});

export const Component: React.FC = () => {
const store = useRemoteData(freshData, {
invalidation: InvalidationStrategy.refetchAfterMillis(2000),
});

return (
<WithRemoteData store={store}>
{(num, isInvalidated) => (
<span style={{ color: isInvalidated ? 'darkgray' : 'black' }}>
{num}
</span>
)}
</WithRemoteData>
);
};

Only Sometimes?

You can enable or disable invalidation dynamically by swapping the invalidation strategy in or out. For instance:

import * as React from 'react';
import {
InvalidationStrategy,
useRemoteData,
WithRemoteData,
} from 'use-remote-data';

var i = 0;
const freshData = (): Promise<number> =>
new Promise((resolve) => {
i += 1;
setTimeout(() => resolve(i), 1000);
});

export const Component: React.FC = () => {
const [autoRefresh, setAutoRefresh] = React.useState(true);
const store = useRemoteData(freshData, {
invalidation: autoRefresh
? InvalidationStrategy.refetchAfterMillis(1000)
: undefined,
});

return (
<div>
<label>
Autorefresh:
<input
type="checkbox"
onChange={(e) => setAutoRefresh(!autoRefresh)}
checked={autoRefresh}
/>
</label>
<br />
<WithRemoteData store={store}>
{(num, isInvalidated) => (
<span
style={{ color: isInvalidated ? 'darkgray' : 'black' }}
>
{num}
</span>
)}
</WithRemoteData>
</div>
);
};

Poll until valid data

Something that comes up sometimes is APIs which require you to poll. use-remote-data supports this through InvalidationStrategy.pollUntil

import * as React from 'react';
import {
InvalidationStrategy,
useRemoteData,
WithRemoteData,
} from 'use-remote-data';

var i = 0;
const freshData = (): Promise<number> =>
new Promise((resolve) => {
i += 1;
setTimeout(() => resolve(i), 1000);
});

export const Component: React.FC = () => {
const store = useRemoteData(freshData, {
invalidation: InvalidationStrategy.pollUntil((x) => x > 2, 1000),
storeName: 'polling-store',
});

return (
<WithRemoteData store={store}>
{(num, notValid) =>
notValid ? <span>invalid data {num}</span> : <span>{num}</span>
}
</WithRemoteData>
);
};

Invalidate on Dependency Change

useRemoteData and useRemoteDatas let you provide a dependencies array (similar to React’s useEffect):

useRemoteData(() => fetchData(), {
dependencies: [props.userId, otherValue],
});

When the dependency array changes, the store invalidates automatically, and triggerUpdate() will re-fetch if needed. (Currently, it compares dependencies by JSON.stringify.)

import * as React from 'react';
import { useRemoteData, WithRemoteData } from 'use-remote-data';

var i = 0;
const freshData = (): Promise<number> =>
new Promise((resolve) => {
i += 1;
setTimeout(() => resolve(i), 1000);
});

export const Component: React.FC = () => {
const [dep, setDep] = React.useState(1);
const store = useRemoteData(freshData, { dependencies: [dep] });

return (
<div>
<button onClick={() => setDep(dep + 1)}>Bump dep</button>
<br />
<WithRemoteData store={store}>
{(num, isInvalidated) => (
<span
style={{ color: isInvalidated ? 'darkgray' : 'black' }}
>
{num}
</span>
)}
</WithRemoteData>
</div>
);
};