前回の記事では、Svelteの概要について紹介しました。そこで、今回はSvelteのStoreについて、どのように定義してどのように使用するかを紹介します。
始めに、SvelteにおけるStoreについて、概要を説明します。
Svelteでは、以下の条件に当てはまるものがStoreとみなされます。
また、subscribe関数および、unsubscribe関数には、それぞれ守るべき条件が定められていますが、ここでは割愛します。詳しく知りたい方は、こちらを参照してください。
Svelteでは、できることが異なる3種類のStoreが定義できます。
次からは、これらのStoreの実装と使い方を説明します。
これらのStoreは、svelte/storeモジュールを使って実装します(このモジュールを使わずに実装することもできます。詳しくはこちらを参照してください)
始めに、最も基本的なwritableのStoreについて、公式サイトの実装例を見ていきます。
外部のモジュールから設定可能な値を持つStoreです。
import { writable } from 'svelte/store'; export const count = writable(0);
Storeの作成はwritable(value: any, (set: (value: any) => void) => () => void)
関数で行ます。第1引数は初期値で、第2引数はsubscriberが0から1に変わった時にのみ実行される関数(省略可能)です。
const count = writable(0, () => { // 何らかの処理(subscribeされている数が0から1に変わった時のみ実行される) return () => { // 何らかの処理(subscribeされている数が0なった時のみ実行される) } });
<script> // 1. import { onDestroy } from 'svelte'; import { count } from './stores.js'; import Incrementer from './Incrementer.svelte'; import Decrementer from './Decrementer.svelte'; import Resetter from './Resetter.svelte'; // 2. let count_value; // 3. const unsubscribe = count.subscribe(value => { console.log(value); count_value = value; }); // 4. onDestroy(unsubscribe); </script> <h1>The count is {count_value}</h1> <Incrementer/> <Decrementer/> <Resetter/>
<script> import { count } from './stores.js'; function decrement() { // 5. count.update(n => n - 1); } </script> <button on:click={decrement}> - </button>
<script> import { count } from './stores.js'; function increment() { // 6. count.update(n => n + 1); } </script> <button on:click={increment}> + </button>
<script> import { count } from './stores.js'; function reset() { // 7. count.set(0); } </script> <button on:click={reset}> reset </button>
それぞれの処理を個別に説明します。
使用例1では、Storeの値を反映させる変数をわざわざ定義しましたが、実はこれは不要です。以下の例のように、$count
で直接参照することができます。
<script> import { count } from './stores.js'; import Incrementer from './Incrementer.svelte'; import Decrementer from './Decrementer.svelte'; import Resetter from './Resetter.svelte'; </script> <!-- $countで直接参照できる --> <h1>The count is {$count}</h1> <!-- $countでbindingできる --> <input bind:value={$count}> <Incrementer/> <Decrementer/> <Resetter/>
ただし、注意点として、このようにStoreを参照するには、Store側がトップレベルスコープで宣言されている必要があります。また、$で始まる名前はすべてStoreを参照しているものとみなされるので、変数名として使用することはできません。
また、通常の変数と同じように、bindingすることができます。
外部のモジュールから参照可能な値を持つStoreです。外部のモジュールから値を更新することはできません。
import { readable } from 'svelte/store'; export const time = readable(new Date(), function start(set) { const interval = setInterval(() => { set(new Date()); }, 1000); return function stop() { clearInterval(interval); }; });
Storeの作成はreadable(value: any, (set: (value: any) => void) => () => void)
関数で行ます。引数の内容はwritableを同じですが、第二引数が必須となっています(Storeを更新する手段がなくなってしまうため)
例では、1秒毎に現在時刻に上書きされます。
<script> import { time } from './stores.js'; const formatter = new Intl.DateTimeFormat('en', { hour12: true, hour: 'numeric', minute: '2-digit', second: '2-digit' }); </script> <h1>The time is {formatter.format($time)}</h1>
使い方については、Writable Storeの使用例2と変わりません。
他の複数のStoreと依存関係を持つStoreです。これらの依存関係が更新されるたびにコールバックが実行されます。
import { readable, derived } from 'svelte/store'; // 1. export const time = readable(new Date(), function start(set) { const interval = setInterval(() => { set(new Date()); }, 1000); return function stop() { clearInterval(interval); }; }); // 2. const start = new Date(); // 3. export const elapsed = derived( time, $time => Math.round(($time - start) / 1000) // 4. );
Storeの作成はderived(a, callback: (a: any, set: (value: any) => void) => void | () => void, initial_value: any)
関数で行ます。
それぞれの処理を個別に説明します。
また、引数には配列を渡すこともできますし、readableと同様に、Set関数のコールバックを渡すこともできます。
import { derived } from 'svelte/store'; const storeC = derived([storeA, storeB], ([$storeA, $storeB], set) => { const interval = setInterval(() => { set($storeA + $storeB); }, 1000); return () => { clearInterval(interval); }; }, 0);
<script> import { time, elapsed } from './stores.js'; const formatter = new Intl.DateTimeFormat('en', { hour12: true, hour: 'numeric', minute: '2-digit', second: '2-digit' }); </script> <h1>The time is {formatter.format($time)}</h1> <p> This page has been open for {$elapsed} {$elapsed === 1 ? 'second' : 'seconds'} </p>
こちらも使い方については、Writable Storeの使用例2と変わりません。
Storeにはsubscribe関数のような必須の関数の他に、独自の関数を定義することもできます。
import { writable } from 'svelte/store'; function createCount() { const { subscribe, set, update } = writable(0); return { subscribe, increment: () => update(n => n + 1), decrement: () => update(n => n - 1), reset: () => set(0) }; } export const count = createCount();
例のように、increment、decrement、resetの関数を含むことができ、外部のモジュールからの利用を制限することができます。
<script> import { count } from './stores.js'; </script> <h1>The count is {$count}</h1> <button on:click={count.increment}>+</button> <button on:click={count.decrement}>-</button> <button on:click={count.reset}>reset</button>
このように、外部のモジュールからは値の参照、increment、decrement、resetのみが可能です。
SvelteのStoreの実装と使い方について紹介しました。Svelte自体にStore機能が内包されているため、Vuexのように外部モジュールを導入しなくて良い点もポイントだと思います。