Store

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

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

Storeの作成

まず最初に、Storeを作成する必要があります。そのために、別のstore.jsファイルを作成しましょう。

// First import createStore function from Framework7 core
import { createStore } from 'framework7/lite';

// create store
const store = createStore({
  // start with the state (store data)
  state: {
    users: [],
    // ...
  },

  // actions to operate with state and for async manipulations
  actions: {
    // context object containing store state will be passed as an argument
    getUsers({ state }) {
      // fetch users from API
      fetch('some-url')
        .then((res) => res.json())
        .then((users) => {
          // assign new users to store state.users
          state.users = users;
        })
    },
    // ...
  },

  // getters to retrieve the state
  getters: {
    // context object containing store state will be passed as an argument
    users({ state }) {
      return state.users;
    }
  }

})

// export store
export default store;

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

createStore(storeParameters)- Storeを作成します

  • storeParameters - オブジェクト。Storeパラメータを持つオブジェクト

メソッドは、作成されたStoreインスタンスを返します。

Storeパラメータ

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

State

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

アクション

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

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

Storeをリアクティブに保つには、代入によって状態を変更する必要があります。たとえば、次のようになります。

// modification of current state property - NOT REACTIVE
state.users.push(...users);

// assignemt to new value - REACTIVE
state.users = [...state.users, ...users];

ゲッター

gettersハンドラーは、ストアの状態からデータを返すために使用されます。また、ストアの状態に基づいて派生状態を計算する必要がある場合(たとえば、アイテムのリストをフィルタリングする場合)にも便利です。

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を作成したので、その使い方を見ていきましょう。

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

<!-- pass store to the App's "store" prop -->
<App store={store}>
  <View main>
    <!-- ... -->
  </View>
</App>
<script>
  import { App, View } from 'framework7-svelte';
  // import our store
  import store from 'path/to/store.js';
</script>

StoreとStateへのアクセス

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

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: {
    // handler receives custom data in second argument
    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;
    },
    // handler context also contains "dispatch" method
    getUsers({ state, dispatch }, { total }) {
      // call other action
      dispatch('setLoading', true);
      fetch(`some-url?total=${total}`)
        .then((res) => res.json())
        .then((users) => {
          state.users = users;
          // call other action
          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コンポーネントで使用してStoreをリアクティブに保つための特別なuseStoreヘルパーがあります(状態/ゲッターの値が変更されたときにコンポーネントを自動更新します)。

useStore(getterName, callback)- ゲッター値を直接返し、状態の更新をサブスクライブします

  • getterName - 文字列 - ゲッターハンドラーの名前
  • callback - 関数 - 依存状態が変更されたときに新しいゲッター値で実行されるコールバック関数。

メソッドは、ゲッターハンドラーの値を返します。

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

useStore(store, getterName, callback)- ゲッター値を直接返し、状態の更新をサブスクライブします

  • store - Storeインスタンス - ゲッターを検索するStoreインスタンス。指定しない場合は、<App>コンポーネントに渡されるデフォルトのStoreが使用されます。
  • getterName - 文字列 - ゲッターハンドラーの名前
  • callback - 関数 - 依存状態が変更されたときに新しいゲッター値で実行されるコールバック関数。

メソッドは、ゲッターハンドラーの値を返します。

次のStoreがある場合

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
  import store from 'path/to/store.js'

  // retrieve "users" getter handler value. Initially empty array
  let users = useStore('users', (value) => users = value);

  onMount(() => {
    // load users when component mounted
    store.dispatch('getUsers');
  });

</script>

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

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;
store.svelte
<script>
  import {
    f7,
    useStore,
    Page,
    Navbar,
    Block,
    Button,
    Preloader,
    List,
    ListItem,
  } from 'framework7-svelte';

  // Subscribe to store getters
  let users = useStore('users', (value) => (users = value));
  let loading = useStore('usersLoading', (value) => (loading = value));

  // Call store action
  const load = () => f7.store.dispatch('loadUsers');
</script>

<Page>
  <Navbar title="Store" />
  <Block strong>
    <p>
      Framework7 comes with a built-in lightweight application state management library - Store. It
      serves as a centralized Store for all the components in an application.
    </p>
  </Block>
  {#if !users}
    <Block class="text-align-center">
      {#if !loading}
        <Button on:click={load} fill round>Load Users</Button>
      {/if}
      {#if loading}
        <Preloader />
      {/if}
    </Block>
  {/if}

  {#if users}
    <List strong outlineIos dividersIos insetMd>
      {#each users as user}
        <ListItem title={user} />
      {/each}
    </List>
  {/if}
</Page>