Store

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

Vueの場合はVuex、Reactの場合はReduxなどのライブラリ固有の状態管理ライブラリや、組み込みのSvelteストア機能を使用できます。ただし、シンプルで済む場合は、Framework7 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)- ストアを作成

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

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

ストアパラメータ

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

状態

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

アクション

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

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

ストアをリアクティブに保つには、状態の変更は代入によって行う必要があります。例:

// 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);
    }
  }
})

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

ストアの使用

ストアを作成したので、次に、その使用方法を見ていきましょう。

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

import React from 'react';
import { App, View } from 'framework7-react';
// import our store
import store from 'path/to/store.js';

export const App = () => {
  // ...
  return (
    {/* pass store to the App's "store" prop */ }
    <App store={store}>
      <View main>
        {/* ... */}
      </View>
    </App>
  )
}

ストアと状態へのアクセス

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

import store from 'path/to/store.js';

console.log(store.state.users);

または、Framework7インスタンスのstoreプロパティにアクセスすることによってアクセスできます。

import { f7 } from 'framework7-react';

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に直接アクセスできます。それ以外の場合は、ゲッターを使用してください。

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

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

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

  • getterName - string - ゲッターハンドラーの名前

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

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

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

  • store - store instance - ゲッターを検索するストアインスタンス。指定しない場合は、<App>コンポーネントに渡されるデフォルトのストアが使用されます。
  • getterName - string - ゲッターハンドラーの名前

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

次のようなストアがある場合

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

たとえば、Reactコンポーネントでは次のように使用する必要があります。

import React, { useEffect } from 'react';
// import special useStore helper/hook
import { useStore, Page, List, ListItem } from 'framework7-react';
// import store
import store from 'path/to/store.js'

export const UsersPage = () => {
  // retrieve "users" getter handler value. Initially empty array
  const users = useStore('users');

  useEffect(() => {
    // load users when component mounted
    store.dispatch('getUsers');
  }, []);

  return (
    <Page>
      <List>
        {users.map((user, index) => (
          <ListItem title={user.name} key={index} />
        ))}
      </List>
    </Page>
  )
}

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

export default () => {
  // Subscribe to store getters
  const users = useStore('users');
  const loading = useStore('usersLoading');

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

  return (
    <Page>
      <Navbar title="Store"></Navbar>
      <Block strong outlineIos insetMd>
        <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>
      {!users && (
        <Block className="text-align-center">
          {!loading && (
            <Button fill round onClick={load}>
              Load Users
            </Button>
          )}
          {loading && <Preloader />}
        </Block>
      )}

      {users && (
        <List strong outlineIos dividersIos insetMd>
          {users.map((user) => (
            <ListItem key={user} title={user} />
          ))}
        </List>
      )}
    </Page>
  );
};