SWR 2.0 on its way! Check https://github.com/vercel/swr/releases/tag/2.0.0-beta.0 for the previous 2.0 beta updates.
💖 Give feedback in discussion: #1932.
Highlights
New isLoading
state (#1928)
Previously, useSWR
only returns a isValidating
state, which is an indicator of both initial requests and automatic & manual revalidations. It includes polling requests and focus revalidations, etc.
But if you need to display an initial skeleton while loading the data, you will have to do something like
const isLoading = typeof data === 'undefined' && !error
...which is a popular pattern in the community. In this case, isValidating
doesn't help much.
In this release, useSWR
, useSWRInfinite
and useSWRImmutable
will return an extra isLoading
state along with the isValidating
state. They will fit to different scenarios:
function Stock() {
const { data, isLoading, isValidating } = useSWR(STOCK_API, fetcher, {
refreshInterval: 3000
});
// If it's still loading the initial data, there is nothing to display.
// We return a skeleton here.
if (isLoading) return <div className="skeleton" />;
// Otherwise, display the data and a spinner that indicates a background
// revalidation.
return (
<>
<div>AAPL ${data}</div>
{isValidating ? <div className="spinner" /> : null}
</>
);
}
In the example above, we display a skeleton while loading the data. After the data is loaded, we show a spinner next to the data whenever we are re-fetching (revalidating):
You can find the full code for this example here: https://codesandbox.io/s/swr-isloading-v8dfpy.
New keepPreviousData
option (#1929)
When doing data fetching based on continuous user actions, e.g. real-time search when typing, keeping the previous fetched data can improve the UX a lot.
In SWR 2.0, there is now a keepPreviousData
option to enable that behavior. Here's a simple search UI:
function Search() {
const [search, setSearch] = React.useState('');
const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
keepPreviousData: true
});
return (
<div>
<input
type="text"
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search..."
/>
<div className={isLoading ? "loading" : ""}>
{data?.products.map(item => <Product key={item.id} name={item.name} />)
</div>
</div>
);
}
With keepPreviousData
enabled, you will still get the previous data
even if you change the SWR key and the data for the new key starts loading again. This improves the visual continuity quite a lot, the search feels smoother after flipping the switch:
CleanShot.2022-04-17.at.02.57.44.mp4
You can find the full code for this example here: https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m.
Breakings
Type InfiniteFetcher
is renamed to SWRInfiniteFetcher
(#1930)
This type was already marked as deprecated in 1.x, and it now removed in this beta. If you are using it, please do the following change:
- import { InfiniteFetcher } from 'swr/infinite'
+ import { SWRInfiniteFetcher } from 'swr/infinite'
What's Changed
- breaking: drop legacy types by @huozhi in #1930
- feat:
isLoading
state and refactor the core by @shuding in #1928 - feat:
keepPreviousData
option by @shuding in #1929 - chore: refactoring by @shuding in #1925
- fix: output mutation cjs bundle with named exports by @huozhi in #1926
Full Changelog: 2.0.0-beta.0...2.0.0-beta.1