スポンサー
Support Framework7

ストア

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

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

    ストアの作成

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

    // まず、CreateStore関数をFramework7 coreからインポートします。
    import { createStore } from 'framework7';
    
    // ストアの作成
    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;

    モジュールを使用していない場合、store.jsファイルは以下のようになります。

    // save store as global object
    window.store = Framework7.createStore({
      state: { /* ... */ },
      actions: { /* ... */ },
      getters: { /* ... */ },
    })

    この例では、次のような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);
        }
      }
    })

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

    ストアの使用

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

    まず最初に、作成したストアをメインのAppインスタンスに渡す必要があります。

    // import our store
    import store from 'path/to/store.js';
    
    const app = new Framework7({
      // storeをアプリの "store "パラメータに渡します。
      store,
      ...
    })
    

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

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

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

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

    import store from 'path/to/store.js';
    
    const app = new Framework7({
      store,
      ...
    })
    
    // 後述します
    console.log(app.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 に直接アクセスし、そうでない場合はゲッターを使用してください。

    ルーターコンポーネントでの使用

    ルーターコンポーネントのコンテキストには、以下のプロパティを持つ $store プロパティがあります。

    • state - ストアの状態です。
    • dispatch - アクションを呼び出す
    • getters - リアクティブゲッターハンドラにアクセスするためのものです。

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

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

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

    <template>
      <div class="page">
        <ul>
          <!-- ゲッターは ".value" プロパティの値を持つ -->
          ${users.value.map((user) => $h`
            <li>${user.name}</li>
          `)}
        </ul>
      </div>
    </template>
    <script>
      export default (props, { $store, $on }) => {
        // users "ゲッターハンドラの値を取得します。最初は空の配列
        const users = $store.getters('users');
    
        $on('pageInit', () => {
          // ページ開始時にユーザーを読み込む
          $store.dispatch('getUsers');
        });
    
        return $render;
      }
    </script>
    

    Examples

    import { createStore } from 'store';
    
    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;
    <template>
      <div class="page">
        <div class="navbar">
          <div class="navbar-bg"></div>
          <div class="navbar-inner">
            <div class="title">Store</div>
          </div>
        </div>
        <div class="page-content">
          ${users.value.length > 0 ? $h`
            <div class="list">
              <ul>
                ${users.value.map((user) => $h`
                <li class="item-content">
                  <div class="item-inner">
                    <div class="item-title">${user}</div>
                  </div>
                </li>
                `)}
              </ul>
            </div>
          ` : $h`
            <div class="block block-strong">
              <a
                href="#"
                class="button button-fill button-preloader ${loading.value ? 'button-loading' : ''}"
                @click=${loadUsers}
              >
                <span class="preloader"></span>
                <span>Load Users</span>
              </a>
            </div>
          `}
        </div>
      </div>
    </template>
    <script>
      export default (props, { $store }) => {
        const loading = $store.getters.loading;
        const users = $store.getters.users;
    
        const loadUsers = () => {
          $store.dispatch('getUsers');
        };
    
        return $render;
      };
    </script>