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>
);
};