儲存

Framework7 內建輕量級應用程式狀態管理程式庫 - Store。它作為應用程式中所有元件的集中式儲存。

你可以使用特定於程式庫的狀態管理程式庫,例如 Vuex for Vue、Redux for React,並使用內建的 Svelte 儲存功能。但如果需要簡單的功能,Framework7 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 - 物件。包含儲存參數的物件

函式傳回已建立的儲存執行個體

儲存參數

現在,我們來看 storeParameters 物件

狀態

state 是包含所有應用程式層級狀態的單一物件,並作為「單一真實來源」。這也表示通常每個應用程式只會有一個儲存。單一狀態樹讓尋找特定狀態片段變得簡單,並讓我們可以輕鬆擷取目前應用程式狀態的快照以進行除錯。

動作

actions 用於修改狀態、進行非同步處理,或呼叫其他儲存動作。動作處理常式會收到一個包含儲存狀態和 dispatch 函式以呼叫其他動作的內容物件。因此,你可以存取 context.store 以存取狀態,或使用 context.dispatch 呼叫其他動作。

作為第二個引數,動作處理常式可能會收到任何自訂資料。

為了保持儲存的反應性,狀態修改應透過指定來完成。例如

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

取得器

可將 Getter 值存取為 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;

Getter 值是包含含有 Getter 處理常式結果的 .value 屬性的靜態物件,因此

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

Getter 與狀態不同,它們旨在具有反應性。因此,當您不需要任何反應性時,您可以直接存取 store.state,否則請使用 Getter。

與 React 元件一起使用

有一個特殊的 useStore 輔助函式,可用於 React 元件中,以保持儲存體反應性(當狀態/Getter 值變更時自動更新元件)。

useStore(getterName)- 直接傳回 Getter 值,並訂閱狀態更新

  • getterName - 字串 - Getter 處理常式的名稱

方法傳回 Getter 處理常式值

如果我們需要從另一個儲存體實例取得 Getter 值,則我們也需要傳遞儲存體

useStore(store, getterName)- 直接傳回 Getter 值,並訂閱狀態更新

  • store - 儲存體實例 - 要從中尋找 Getter 的儲存體實例。如果未指定,則會使用傳遞給 <App> 元件的預設儲存體。
  • getterName - 字串 - Getter 處理常式的名稱

方法傳回 Getter 處理常式值

如果我們有下列儲存體

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 輔助函式/hook,因此當使用者載入時,元件將自動更新。

範例

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