Svelteのcontextはなぜstoreと共に使う必要があるのか
はじめに
Svelte初学者です。
Svelteでwebアプリを作っている中で、グローバルな状態管理の方法に大きく2つの方法があることを知りました。
それが今回のテーマである以下の2つです。
これらの使い所はどのように分けたら良いのか、なぜ以下の公式ドキュメントのようにcontextとstoreを共に使う必要があるのかを理解し、整理するために記事を書きました。
前提
"svelte": "^4.0.5",
"@sveltejs/kit": "^1.20.4"
contextとstoreの違いとは?
まず、簡単に違いを整理します。
「リアクティブ」かどうか
- contextはリアクティブではない。
- storeはリアクティブである。
アクセス可能な範囲
- あるコンポーネントで定義されたcontextは、その下の階層にある子コンポーネントのみがアクセス可能。
- storeはアプリケーションのどこからでもアクセス可能。
実際に動きを見てみる
試しに、数字と、クリックするとその数字に+1するボタンだけのコンポーネントを作ってみます。
数字はグローバルで状態管理された値を利用します。
context
以下はParentコンポーネントで定義したcontextをChildコンポーネントでも呼び出している実装です。
Childコンポーネントでは、Parentコンポーネントで作成したcontextを取得できていることがわかります。
また、buttonクリックでsetContextを利用して値の更新を試みていますが、変更がDOMに反映されることはありません。
<script lang="ts">
import { setContext } from 'svelte';
const count = setContext('sampleCount', 0);
function updateCount() {
setContext('sampleCount', count + 1);
}
</script>
<button on:click={updateCount}>button</button>
<div>
<p>Parent: {count}</p>
<slot />
</div>
<script lang="ts">
import { getContext } from 'svelte';
const count = getContext<number>('sampleCount');
</script>
<div>
<p>Child: {count}</p>
</div>
<script lang="ts">
import Child from '$lib/components/contextOnly/child.svelte';
import Parent from '$lib/components/contextOnly/parent.svelte';
</script>
<Parent>
<Child />
</Parent>
store(ルートコンポーネントはcontextの場合と同様なので省略)
以下は先ほどcontextでやったことと全く同じことをstoreで実現しています。
しかし、contextを利用した時とは異なり、クリックするたびにDOMが更新されます。
これは、繰り返しになりますがsvelteにおいてstoreがリアクティブな値であるためです。
import { writable } from 'svelte/store';
export const count = writable(0);
<script lang="ts">
import { count } from './store';
function updateCount() {
count.set(($count += 1));
}
</script>
<button on:click={updateCount}>button</button>
<div >
<p>Parent: {$count}</p>
<slot />
</div>
<script lang="ts">
import { count } from './store';
</script>
<div>
<p>Child: {$count}</p>
</div>
contextと共にstoreを使う(ルートコンポーネントはcontextの場合と同様なので省略)
contextと共にstoreを利用した場合も、今回の例だと動きはstoreと同様です。
import { writable, type Writable } from 'svelte/store';
import { getContext, setContext } from 'svelte';
type Context = Writable<number>;
export function setCount() {
const count = writable<number>(0);
setContext('count', count);
}
export function getCount() {
return getContext<Context>('count');
}
<script lang="ts">
import { setCount, getCount } from './contextStore';
setCount();
const count = getCount();
function updateCount() {
$count += 1;
}
</script>
<button on:click={updateCount}>button</button>
<p>Parent: {$count}</p>
<slot />
<script lang="ts">
import { getCount } from '../context';
const count = getCount();
</script>
<div>
<p>Child: {$count}</p>
<slot />
</div>
contextはなぜstoreと共に使う必要があるのか
ある程度予測はつくかと思いますが・・
結論、contextとstoreを共に使うのはstoreのようにリアクティブにかつ、contextのように限定的にある値を参照したいときが良いのだと考えます。
そして、これは多くのケースで当てはまると思います。
また、正直contextは単体で使う場面があまり想像できませんでした。ご存知の方がいればいただけますと幸いです。
今回は以上です。
参考
Discussion