跨端全局状态管理工具 小程序作为多页应用,订阅发布事件无法在多页之间触达,为了实现小程序的全局状态管理,我们需要将状态维护在 getApp 上,并对其进行监听,通过 getApp 这个统一的内存进行通信,
tnpm i @sstore/core @sstore/react
const userInfo = {
state: {
name: '小明',
age: 10
},
reducers: {
foo:(prevState, payload) => prevState.age + payload,
},
actions: (dispatch) => ({
async addAge(num) {
await delay(1000);
this.foo(num);
},
})
};
const models = {
userInfo,
};
import { createStore } from "@sstore/core";
const store = createStore({ models, globalSpace: window });
import { useSelector } from "@sstore/react";
const Index = function ({ query, history, ...rest }) {
// 需要将创建的 store 作为参数传入
const state = useSelector((state) => state?.userInfo)(window.store);
//...
}
import { useDispatch } from "@sstore/react";
const Index = function ({ query, history, ...rest }) {
const { setState, foo } = useDispatch("userInfo")(window.store);
//...
}
这里内置了两个 reducers 方法,可以直接取来使用:
用来改变 store 中存储的状态,并触发更新。 内部实现等于如下reducer的实现:
setState = (state: State, payload: any) => {
return {
...state,
...payload,
};
};
用来改变 store 中存储的状态,并不触发更新。 使用方法和 setState 一样,唯一的区别是不会触发监听更新。
注意:为了避免在多人开发时,已经使用的情况下被误覆盖,setState 和 setStateNoRefresh 方法是不允许被覆盖的,也就是说如果你在 reducers 中声明了同名的 setState,那么你声明的那个setState 将会被覆盖掉。
用来创建store实例的方法。
属性名 | 类型 | 含义 |
---|---|---|
models | object | 用户创建的models |
globalSpace | object | 可以全局访问的对象 |
storageKey | string | storage 存储 Key 自定义标识 |
models是用户定义的对象,key作为mdel的唯一名称,通过key来获取对应的model; value是指定的结构,有三个属性:
any
用户定义的模型初始状态
eg:
const userInfo = {
state: {
name: '小明',
age: 10
}
};
{ [string]: (state, payload) => any }
一个改变该模型状态的函数集合。这些方法以模型的上一次 state 和一个 payload 作为入参,在方法中使用可变的方式来更新状态。 这些方法应该是仅依赖于 state 和 payload 参数来计算下一个 state 的纯函数。对于有副作用的函数,请使用 actions。
eg:
const userInfo = {
state: {
name: '小明',
age: 10
},
reducers: {
foo:(prevState, payload) => prevState.age + payload,
}
};
(dispatch) => ({ [string]: (payload, rootState) => void })
一个可以处理该模型副作用的函数集合。这些方法以 payload 和 rootState 作为入参,适用于进行异步调用、模型联动等场景。在 actions 内部,通过调用 this.foo 来更新模型状态:
eg:
const userInfo = {
state: {
name: '小明',
age: 10
},
reducers: {
foo:(prevState, payload) => prevState.age + payload,
},
actions: (dispatch) => ({
async addAge(num, rootState) {
await delay(1000);
this.foo(num);
},
})
};
ps: 如果actions和reducers重名,则以reducers为先。 你可以在actions里调用其它model的reducers和actions方法,eg:
const userInfo = {
state: {
name: '小明',
age: 10
},
reducers: {
foo:(prevState, payload) => prevState.age + payload,
},
};
const person = {
state: {
name: '小明',
age: 10
},
actions: (dispatch) => ({
addAge(num, rootState) {
dispatch.userInfo.foo(num);
},
})
};
<T extends Models, S>(selector: (state: TStates<T>) => any, options?: Options) => (store: TStores<T>) => state
属性名 | 类型 | 含义 |
---|---|---|
equalityFn | (a: T, b: T) => boolean | 比较方法 |
sign | any | 监听标识 |
callback | ({ value }: CallbackArgs) => void | 更新回调 |
通过该 hooks 获取store 指定的数据,你可以精准的指定获取某一个属性。且每次调用改方法时,会注册当前页面监听的属性,只有在监听的属性发生变动时,才会触发当前页面的rerender。 eg:
const name = useSelector((state) => state?.userInfo.name, {})(window.store);
因为有了这一机制,我们可以精准更新依赖组件,性能更佳。
我们将每个模型的state都存在你传入的globalSpace.state上,
不要直接修改他们,因为这样会使得你的代码不可维护,引起变更的地方变得不可控。像下边这样,这同样可以触发使用到他们的页面的rerender, eg:
// 不要这样做
globalSpace.state.userInfo.name = '李三';
(moduleName: string) => Setter
通过该 hooks 获取store 指定的 reducer 或者 action 方法,默认会有 setState, setStateNoRefresh 方法。
eg:
const { setState, foo } = useDispatch('userInfo')(window.store);
(path: string | string[], value) => void
通过该方法,可以直接更新 store,当 path 传 'root' 时,直接更新整个 store 根节点,各个更新监听会被正常触发
(path: string | string[]) => void
通过该方法,可以获取 store 指定节点内容,当 path 传 '' 时,直接返回整个 store 根节点的内容
(key: 'storeChange', cb: ({newV, key, unRerenderPage}) => void) => void
通过该方法,可以监听 store 的每次更新
(key: 'storeChange', obj: {newV, key, unRerenderPage}) => void
通过该方法,可以触发 storeChange 的所有监听
useDispatch 方法接受 models,可以对模型的reducers、actions方法做出提示,eg:
import { useDispatch } from "@sstore/react";
import models from 'models';
const Index = function ({ ...props }) {
const { setState, foo } = useDispatch<typeof models, 'example'>('example')(window.store);// 这里可以获得提示
//...
}
export default Index;
const timeMiddleware = ({getState}) => (next) => (action) => {
console.log('time',action, new Date().getTime());
next(action);
}