ストア

Framework7には、軽量のアプリケーション状態管理ライブラリであるStoreが組み込まれています。これは、アプリケーション内のすべてのコンポーネントのための一元化されたStoreとして機能します。

VueにはVuex、ReactにはReduxのようなライブラリ固有の状態管理ライブラリを使用したり、Svelteの組み込みストア機能を使用することができます。しかし、シンプルなものが必要な場合には、Framework7 Storeが適しているでしょう。

    ストアの作成

    まず最初にストアを作成しなければなりません。そのために別の store.js ファイルを作成しましょう。

    // まず、CreateStore関数をFramework7 coreからインポートします。
    import { createStore } from 'framework7/lite';
    
    // ストアの作成
    const store = createStore({
      // 状態(ストアデータ)から始める
      state: {
        users: [],
        // ...
      },
    
      // 状態を操作するアクションと、非同期操作のためのアクション
      actions: {
        // ストアの状態を含むコンテキストオブジェクトが引数として渡される
        getUsers({ state }) {
          // APIからユーザーを取得する
          fetch('some-url')
            .then((res) => res.json())
            .then((users) => {
              // 新しいユーザーをストアに割り当てる state.users
              state.users = users;
            })
        },
        // ...
      },
    
      // 状態を取得するゲッター
      getters: {
        // ストアの状態を含むコンテキストオブジェクトは、引数として渡されます。
        users({ state }) {
          return state.users;
        }
      }
    
    })
    
    // ストアのエクスポート
    export default store;

    この例では、次のようなAPI関数を使用しました。

    createStore(storeParameters)- クリエイトストア

    • storeParameters - object. ストアのパラメータを持つオブジェクト

    メソッドは作成されたストアのインスタンスを返します

    ストアのパラメータ

    では、storeParametersオブジェクトを見てみましょう。

    状態

    stateは、すべてのアプリケーションレベルの状態を含む単一のオブジェクトで、「単一の真実の情報源」として機能します。これは、通常、各アプリケーションに対して1つのストアしか持たないことを意味します。単一のステートツリーを使用すると、特定のステートを簡単に見つけることができ、デバッグ目的で現在のアプリケーションの状態のスナップショットを簡単に取ることができます。

    アクション

    アクション」は、ステートを変更したり、非同期操作をしたり、他のストアアクションを呼び出したりするのに使用します。アクションハンドラーは、ストアの状態と、他のアクションを呼び出すためのディスパッチメソッドを持つコンテキストオブジェクトを受け取ります。つまり、context.storeにアクセスしてステートにアクセスしたり、context.dispatchで他のアクションを呼び出したりすることができます。

    第2引数として、アクションハンドラは任意のカスタムデータを受け取ることができます。

    ストアのリアクティブ性を保つために、ステートの変更は代入で行う必要があります。例えば、以下のようになります。

    // 現在の状態のプロパティの変更 - リアクティブではない
    state.users.push(...users);
    
    // 新しい値への代入 - リアクティブ
    state.users = [...state.users, ...users];

    ゲッター

    ゲッター`ハンドラーは、ストアの状態からデータを返すために使用します。また、アイテムのリストをフィルタリングするなど、ストアの状態に基づいて派生した状態を計算する必要がある場合にも便利です。

    const store = createStore({
      state: {
        users: [
          { id: 1, name: '...', registered: true },
          { id: 2, name: '...', registered: false }
        ]
      },
      getters: {
        registeredUsers: ({ state }) => {
          return state.users.filter((user) => user.registered);
        }
      }
    })

    ゲッターハンドラーもコンテキストオブジェクトを受け取りますが、ストアの状態のみを受け取ります。たとえば、ゲッターから他のアクションを呼び出すことはできません。

    ストアの使用

    ストアを作成したら、次はそれをどう使うかを考えましょう。

    まず最初に、作成したストアをアプリのメインコンポーネントに渡す必要があります。

    <!-- storeをアプリの "store "プロップに渡す -->
    <App store={store}>
      <View main>
        <!-- ... -->
      </View>
    </App>
    <script>
      import { App, View } from 'framework7-svelte';
      // ストアのインポート
      import store from 'path/to/store.js';
    </script>
    

    ストアとステートへのアクセス

    作成したストアのインスタンスを参照することで、ストア(とその状態)に直接アクセスすることができます。

    import store from 'path/to/store.js';
    
    console.log(store.state.users);

    または、Framework7 のインスタンスの store プロパティにアクセスします。

    import { f7 } from 'framework7-svelte';
    
    console.log(f7.store.state.users);
    

    アクションの実行

    アクションを呼び出すには、呼び出すアクションの名前を指定して store.dispatch メソッドを呼び出す必要があります。

    以下のようなストアアクションがあるとします。

    const store = createStore({
      // ...
      actions: {
        // ハンドラーは第2引数にカスタムデータを受け取ります
        getUsers({ state }, { total }) {
          fetch(`some-url?total=${total}`)
            .then((res) => res.json())
            .then((users) => {
              state.users = users;
            })
        },
      },
      // ...
    })

    とすると、store.dispatchメソッドを呼び出さなければなりません。

    import store from 'path/to/store.js';
    
    // call 'getUsers' actions
    store.dispatch('getUsers', { total: 10 })

    アクション・ハンドラの中で、別のアクション・ハンドラを呼び出したいとします。

    const store = createStore({
      // ...
      actions: {
        setLoading({ state }, isLoading) {
          state.isLoading = isLoading;
        },
        // ハンドラーコンテキストには "dispatch" メソッドも含まれています
        getUsers({ state, dispatch }, { total }) {
          // 他のアクションを呼び出す
          dispatch('setLoading', true);
          fetch(`some-url?total=${total}`)
            .then((res) => res.json())
            .then((users) => {
              state.users = users;
              // 他のアクションを呼び出す
              dispatch('setLoading', false);
            })
        },
      },
      // ...
    });
    

    ゲッター

    ゲッターの値は store.getters オブジェクトのスタティックなプロパティとしてアクセスできます。

    const store = createStore({
      state: {
        count: 10,
      },
      getters: {
        count({ state }) {
          return state.count;
        },
        double({ state }) {
          return state.count * 2;
        },
      },
    });
    import store from 'path/to/store.js';
    
    const count = store.getters.count;
    const double = store.getters.double;

    ゲッター値は、ゲッターハンドラの結果を含む .value プロパティを持つ静的オブジェクトです。

    console.log(count.value); // -> 10
    console.log(double.value); // -> 20

    ゲッターはステートとは異なり、リアクティブであることが求められます。反応性を必要としない場合は、store.stateに直接アクセスし、そうでない場合はゲッターを使用してください。

    Svelteコンポーネントでの使用

    Svelteコンポーネントで使用するための特別なuseStoreヘルパーがあり、ストアのリアクティブ性を保つことができます(ステートやゲッターの値が変更されたときにコンポーネントを自動更新します)。

    useStore(getterName, callback)- 直接ゲッター値を返し、状態の更新を購読します。

    • getterName - string - ゲッターハンドラの名前。
    • callback - function - 依存するステートが変更されたときに、新しいゲッター値とともに起動されるコールバック関数。

    ゲッターハンドラの値を返すメソッド

    他のストアインスタンスからゲッター値を取得する必要がある場合は、ストアを渡す必要があります。

    useStore(store, getterName, callback)- メソッドは直接ゲッター値を返し、状態の更新を購読します。

    • store - store instance - ゲッターを参照するストア インスタンス。指定されていない場合は、<App>コンポーネントに渡されたデフォルトのストアが使用されます。
    • getterName - string - ゲッターハンドラーの名前です。
    • callback - function - 依存関係の状態が変更されたときに、新しいゲッターの値とともに起動されるコールバック関数です。

    ゲッターハンドラの値を返すメソッド

    以下のようなストアがあるとします。

    const store = createStore({
      state: {
        users: [],
      },
      actions: {
        getUsers({ state }) {
          // ...
        },
      },
      getters: {
        users({ state }) {
          return state.users;
        }
      },
    });

    例えば、Svelteコンポーネントでは、以下のように使用します。

    <Page>
      <List>
        {#each users as user}
          <ListItem title={user.name} />
        {/each}
      </List>
    </Page>
    <script>
      import { onMount } from 'svelte';
      // import special useStore helper/hook
      import { useStore, Page, List, ListItem } from 'framework7-svelte';
      // インポートストア
      import store from 'path/to/store.js'
    
      // retrieve "users" ゲッターハンドラの値。最初は空の配列
      let users = useStore('users', (value) => users = value);
    
      onMount(() => {
        // コンポーネント実装時にユーザーをロードする
        store.dispatch('getUsers');
      });
    
    </script>

    Framework7のuseStoreヘルパー/フックを使用しているので、ユーザーがロードされるとコンポーネントが自動更新されます。

    Examples

    import { createStore } from 'framework7/lite';
    
    const store = createStore({
      state: {
        loading: false,
        users: [],
      },
      actions: {
        getUsers({ state }) {
          state.loading = true;
          setTimeout(() => {
            state.users = ['User 1', 'User 2', 'User 3', 'User 4', 'User 5'];
            state.loading = false;
          }, 3000);
        },
      },
      getters: {
        loading({ state }) {
          return state.loading;
        },
        users({ state }) {
          return state.users;
        },
      },
    });
    
    export default store;
    <Page>
      <Navbar title="Store" />
      {#if users.length}
        <List>
          {#each users as user}
            <ListItem title={user} />
          {/each}
        </List>
      {:else}
        <Block strong>
          <Button fill preloader loading={loading} onClick={loadUsers}>
            Load Users
          </Button>
        </Block>
      {/if}
    </Page>
    <script>
    import { useStore, Page, Navbar, Block, List, ListItem, Button } from 'framework7-svelte';
    import store from './store';
    
    let loading = useStore('loading', (value) => loading = value);
    let users = useStore('users', (value) => users = value);
    
    const loadUsers = () => {
      store.dispatch('getUsers');
    };
    </script>