存储

Framework7 随附一个内置的轻量级应用程序状态管理库 - 商店。它用作应用程序中所有组件的集中商店。

可以使用库特定的状态管理库,如 Vue 的 Vuex、React 的 Redux,并使用内置的 Svelte 商店功能。但是,如果需要一些简单的东西,那么 Framework7 商店可能很合适。

创建商店

首先,我们需要创建商店。我们为其创建单独的 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];

getter

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

getter 处理程序也接收一个上下文对象,但仅包含存储状态。例如,不可能从 getter 调用其他动作。

使用存储

现在我们已经创建了存储,我们再来了解如何使用它。

首先,我们需要将创建的存储传递到主 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

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 的值是带有 .value 属性的静态对象,其中包含 getter 处理程序的结果,因此

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

getter 与状态不同,getter 的目的是响应式的。因此,当你不需要任何响应性时,你可以直接访问 store.state,否则,请使用 getter。

与 React 组件一起使用

对于在 React 组件中使用,有一个特殊的 useStore 帮助器,用于保持存储的响应性(当状态或 getter 值更改时自动更新组件)。

useStore(getterName)- 直接返回 getter 的值并订阅状态更新

  • getterName - string - getter 处理程序的名称

方法返回 getter 处理程序的值

如果我们需要从另一个存储实例获取 getter 的值,那么我们还需要传递存储

useStore(store, getterName)- 直接返回 getter 的值并订阅状态更新

  • store - store 实例 - 要从中查找 getter 的 store 实例。如果没有指定,则将使用传递给 <App> 组件的默认存储。
  • getterName - string - 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 帮手/挂钩,因此当用户加载时,组件会自动更新。

示例

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