SWR v2 をリリースしました
2022/12/13 @koba04
メンテナとして関わっていた SWR v2 がリリースされましたので紹介したいと思います。
各機能の細かい紹介については、リリースブログを確認してください。日本語翻訳も行ったので日本語で読むこともできます。
https://swr.vercel.app/ja/blog/swr-v2
ここでは、ざっくりと補足を書きたいと思います。
Mutation 周り
useSWRMutation
一番わかりやすいのは、新しい useSWRMutation
という Hook が追加されたことです。swr/mutation
から import できます。
import useSWRMutation from 'swr/mutation'
async function sendRequest(url, { arg }) {
return fetch(url, {
method: 'POST',
body: JSON.stringify(arg)
})
}
function App() {
const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest)
return (
<button
disabled={isMutating}
onClick={() => trigger({ username: 'johndoe' })}
>{
isMutating ? 'Creating...' : 'Create User'
}</button>
)
}
isMutating
により mutation 実行中の状態が取得できるのはこれまでにできなかったことです。
その他は mutate
とほぼ同等ですが Hook として宣言的なインターフェイスで利用できるようになっています。詳しくは下記を。
https://swr.vercel.app/blog/swr-v2#useswrmutation
Optimistic UI
Optimistic Update 自体は v1.2.0 から可能だったのですが、関数が渡せるようになったり柔軟に指定できるようになりました。
const { mutate, data } = useSWR('/api/todos')
return <>
<ul>{/* Display data */}</ul>
<button onClick={() => {
mutate(addNewTodo('New Item'), {
optimisticData: [...data, 'New Item'],
})
}}>
Add New Item
</button>
</>
https://swr.vercel.app/blog/swr-v2#optimistic-ui
エラー時のロールバックなんかも自動でやってくれるので簡単に使うことができるのではないかと思います。
Mutate Multiple Keys
個人的には、これが一番嬉しいのではと思っていたのですが、言及している人が少ない気がします。
グローバルな mutate
(useSWR
経由で取得するものではない) にフィルタリング関数を渡すことで、柔軟に revalidate ができるようになりました。
import { mutate } from 'swr'
// Or from the hook if you have customized your cache provider:
// { mutate } = useSWRConfig()
mutate(
key => typeof key === 'string' && key.startsWith('/api/item?id='),
undefined,
{ revalidate: false }
)
上記の例では、/api/item
のエンドポイントに関するデータを全て revalidate するようなケースが想定されています。
他にも配列をキーにしている場合には配列の一部がマッチするもの全てを revalidate することも可能です。この関数には全てのキーが渡されるのでキーの型チェックは必須です。
// ['user', 1], ['user', 2] のようなキーで 'user' に関するもの全てを revalidate する
mutate(key => Array.isArray(key) && key[0] === "user")
キャッシュをクリアすることも可能です。
const clearCache = () => mutate(
() => true,
undefined,
{ revalidate: false }
)
// ...clear cache on logout
clearCache()
https://swr.vercel.app/blog/swr-v2#mutate-multiple-keys
cache.clear()
などでキャッシュを直接更新することは推奨されてないのでキャッシュをクリアしたい場合はこちらを利用してください。
SWRDevTools
SWR v2 に上げて、拡張をインストールするだけで使えます。 (v1 でもコンポーネントをアプリケーションに差し込むことで利用可能です)
https://swr.vercel.app/blog/swr-v2#swr-devtools
Preloading Data
どこでも呼べるのでレンダリング前やページ遷移前に呼んでおくことができます。
import useSWR, { preload } from 'swr'
const fetcher = (url) => fetch(url).then((res) => res.json())
// You can call the preload function in anywhere
preload('/api/user', fetcher)
function Profile() {
// The component that actually uses the data:
const { data, error } = useSWR('/api/user', fetcher)
// ...
}
export function Page () {
return <Profile/>
}
https://swr.vercel.app/blog/swr-v2#preloading-data
isLoading
そのままですね。
import useSWR from 'swr'
function Profile() {
const { data, isLoading } = useSWR('/api/user', fetcher)
if (isLoading) return <div>loading...</div>
return <div>hello {data.name}!</div>
}
fallbackData
や後述する keepPreviousData
オプションを使う場合には少しトリッキーな形になるので注意が必要です。
下記のページで挙動を確認できます。
https://swr.vercel.app/docs/advanced/understanding
isValidating
と isLoading
との使い分けについては、リンク先の動画が非常にわかりやすいのでチェックしてみてください。
Preserving Previous State
キーが変化したときに、通常だと対応するデータがないので undefined
が返りますが、以前のキーに対応するデータを返すオプションです。
文字で説明するのが難しいやつですが、動画を見ると意味がすぐわかると思うので、これもリンク先の動画をチェックしてみてください。
https://swr.vercel.app/blog/swr-v2#preserving-previous-state
これを使うことでユーザー体験を改善できるケースは多いと思います。
Extending Configurations
SWRConfig
のコンポーネントの value
が関数を受け取るようになったので、親の SWRConfig
の設定を元に拡張できるようになりました。
https://swr.vercel.app/blog/swr-v2#extending-configurations
Improved React 18 Support
内部的に、useSyncExternalStore
や startTransition
を使っているので利用者は特に意識することなく Concurrent Features の恩恵を受けることができます。
Shim も含まれているので React 17 でも動作します。
破壊的変更
Fetcher No Longer Accepts Multiple Arguments
キーに配列を渡した場合、これまでは fetcher に個別の引数として渡されていたものが配列として渡されるようになります。 今回で、これが最も影響のありそうな変更です。
- useSWR([1, 2, 3], (a, b, c) => {
+ useSWR([1, 2, 3], ([a, b, c]) => {
assert(a === 1)
assert(b === 2)
assert(c === 3)
})
これを変えた理由としては、fetcher の引数に対して拡張性を持たせたいからです。これを利用した機能拡張も検討中です。
Global Mutate No Longer Accepts a getKey Function
mutate
がフィルタ関数を受け取るようになったことで、関数を渡した場合にはキーを返す関数ではなく、フィルタ関数として扱われるようになります。
- mutate(() => '/api/item') // a function to return a key
+ mutate('/api/item') // to mutate the key, directly pass it
New Required Property keys() for Cache Interface
Cache
のインターフェイスに keys()
が必須になりました。これは mutate
で複数のキーを処理できるようにするためにキーの一覧を取得できる必要があるためです。
Changed Cache Internal Structure
キャッシュの内部構造が変わりました。キャッシュのデータを直接扱うのは可能な限り避けた方がいいです。 LocalStorage に保存してるような場合は、バージョンアップ時に注意が必要です。
SWRConfig.default Is Renamed as SWRConfig.defaultValue
これはそのままです。
- SWRConfig.default
+ SWRConfig.defaultValue
Type InfiniteFetcher Is Renamed as SWRInfiniteFetcher
これもそのままです。
- import type { InfiniteFetcher } from 'swr/infinite'
+ import type { SWRInfiniteFetcher } from 'swr/infinite'
Avoid Suspense on Server
suspense: true
オプションを SSR で使う場合には必ず、fallbackData
や fallback
により対応するデータが含まれている必要があります。
ES2018 as the Build Target
ビルドターゲットが ES2018 になりました。なぜ、ES2018 かというと Object Rest Spread {...a, ...b}
をそのまま利用したかったです。これまで Object.assign
が使われていたのですが、SSR で Spread Operator の方がかなりパフォーマンスがいいことがわかったのでそうなりました。詳しくは https://github.com/vercel/swr/pull/2249 を。
元々のターゲットが ES5 であったため、バンドルサイズもかなり小さくなり、結果的に色々と機能が追加されたにも関わらず、バンドルサイズは v1 よりも小さくなりました。
https://bundlephobia.com/package/swr@2.0.0
個人的には、バンドルサイズが増えなかったのがとても嬉しかったです。
Next.js 13 によりどうなる?
2022 年の Next.js Conf において、Next.js の app/
ディレクトリで提供される React Server Components のような機能により SWR のユースケースもカバーできるという話があったので、SWR どうなるのと感じた人は自分以外にもいたのではないでしょうか?そのため、今回のリリースブログにそれについても足してもらいました。
結論としては、よりよい UX を提供していくためのライブラリとしてこれからも SWR の開発は継続されます。実際、自動的な revalidation など SWR でしかカバーできないケースも存在します。
ただ、SWR を React Server Components 上で動かすのは難しいため、Client Components においてのデータ取得のライブラリとして開発されていくことが想定されています。
そのため、client-only
を付与することも検討されています。
https://github.com/vercel/swr/issues/2221
おまけ
Middleware によるコアのスリム化
SWR は v1 で Middleware の仕組みを導入しています。
https://swr.vercel.app/docs/middleware
これにより、fetcher を拡張したり前後に処理を挟むことが可能になっています。Middleware は利用者に対してもメリットがありますが SWR 自体に対してもメリットがある仕組みになっています。
例えば、swr/immutable
や swr/infinite
、今回追加された swr/mutation
は全て Middleware として実装されています。そのため、機能を追加した場合でも SWR のコア自体に対して複雑性が高まることはなく、バンドルサイズも増えることがありません。
また今回追加された preload
の実装を担当したのですが、実はこれも内部的には Middleware として実装して、Builtin-Middleware として組み込まれています。
今回のリリースで主にやったこと
前回の v1 のリリース時はコントリビュータとして開発に参加しておりブログで感謝の言葉をもらって嬉しかったのですが、今回はメンテナとしてリリースに携わりました。
v2 では preload
や幾つかの機能実装やバグ修正をやりつつ、最も力を注いだのがドキュメント(ブログ含む)です。
ブログは Author の一人になりました。 (感謝の部分を書いたのは自分ではないですが、自分で翻訳して何とも言えない気持ちになりました)
https://github.com/vercel/swr-site/pull/325
上記の PR にある v2 のドキュメント反映は大体担当しました。リリースブログも大体書いたのですが、マージ前に Shu さんが手直してくれてとてもわかりやすくなりました。
今回は Mutation 周りのドキュメントの構成がかなり変わっています。これまでは API のインターフェイスもわかりにくかったのですが、その辺りを明示的に書いたりユースケースを追加したりしています。
またこれは v2 に直接関係ないのですが、「Understanding SWR」というページも追加させてもらいました。
https://swr.vercel.app/docs/advanced/understanding
SWR を最初に触った時にどういった値が返ってくるのかがわからないと思った記憶があり、v2 で isLoading
や keepPreviousData
など新しいパターンが増えるので図にして表現してみました。
名前が示す通り、ここには他にも色々とコンテンツを追加していきたいなと思っています。
SWR のドキュメントはまだまだよくできる部分もあるのでこの辺りは今後もやっていきたいと思っています。
また、DevTools がないという声を Issue で目にしたので、SWRDevTools を作ってみました。
https://swr-devtools.vercel.app/
自分が必要だと思って作ったわけではないのでどこまで使いやすいかは未知数ですが、フィードバックもらいながらよくしていければなと思っているのでフィードバック歓迎です!
https://github.com/koba04/swr-devtools
アプリケーションに手を入れることなく、拡張をインストールして開いたら確認できる手軽な形にはなっていると思います。 拡張作るの初めてでライフサイクルを完全に理解できておらず、開きっぱなしで放置しておくと接続が切れる不具合があるので、そのうち修正したいなと思っています。 Shu さんが足してくれた UI がカッコいい Network Panel については、不具合がまだあるので Experimental という形になっています。
SWRDevTools から Mutation できる機能については、あった方がいいのかなと思いつつ使いたいユースケースがあまり思いつかなかったので入れてません。
最後に
SWR はとあるアプリケーションのレビューをさせてもらった時に初めて使われているのを見て面白いなと思い趣味でコントリビュートを始めたのがきっかけです。 コードからの学びも大きくチームメンバーも素晴らしくとても楽しいプロジェクトなので、今後もメンバーとして貢献していきたいなと思っています。