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