Inula-X
Inula-X 是集成在 openinula 框架中用于状态管理的库。它能集中式地存储、管理应用中所有组件的状态,并通过相应的规则 (actions
) 保证状态 (state
) 以可预测的方式发生变化。
核心概念 -- Store
对象
功能介绍
Store
是 Inula-X 用于存储的对象,一个 Store 对象由 id
, state
, actions
, computed
4个部分组成,并通过 id
来标识不同的对象。注意 state
, actions
, computed
的属性或方法不能重名。Store
对象可以由 createStore
方法创建,下面是一个Store对象的整体示例:
示例
const store = createState({
// id 用于标识一个Store对象
id: 'user',
// state 用于存放数据
state: {
data: {
list: [
{ id: 1, name: 'liming', age: 18 },
{ id: 2, name: 'xiaohong', age: 20 },
],
},
loading: false,
selectUsers: [1],
},
// action 用于修改state中的数据
actions: {
addUser(state, payload) {
state.data.list.push(payload);
},
addSelectedUser(state, payload) {
state.selectUsers.push(payload);
},
setLoading(state) {
state.loading = true;
},
},
// computed 用于获取state状态变量的计算属性
computed: {
users: state => state.data.list,
userCount(states) {
// 通过this获取其他computed的值
return this.users.length;
},
},
});
state
state
是 Store
中存储数据的部分,通常把组件间需要共享的状态存放在 state
中。state
属性是响应式的,当某一个 state
的数据被修改,使用到这个数据的组件会更新,state
具有精细的数据监听的粒度,例如:组件A使用了 name
属性,组件B使用了 age
属性,当 name
属性被修改时,组件A会重选渲染,组件B则不会重新渲染。
可通过 $s
访问 Store 对象中的 state
,也可以直接通过 state
属性名称访问,两者的效果相同。
注意:state 的修改只应该在
actions
中进行,不应该在actions
之外修改state
。
const getStore = createStore({
id: 'value',
state: {
val: 0,
},
});
function App() {
const store = getStore();
return (
<>
<h1>{store.val}</h1>
<h1>{store.$s.val}</h1>
</>
);
}
actions
actions
是 state
中处理数据逻辑的部分,对 state
的修改都应该在 actions
中进行。
可通过 $a
访问 Store 对象中的 actions
,也可以直接通过 actions
函数名称访问,两者的效果相同。
const getStore = createStore(
{
id: 'value',
state: {
val: 0,
},
actions: {
plusOne(state) {
state.val += 1;
},
plusValue(state, value) {
state.val += value;
},
},
},
);
function App() {
const store = getStore();
return (
<>
<h1>{store.val}</h1>
{/* 修改state值 */}
<button onClick={() => store.plusOne()}>+1</button>
<button onClick={() => store.$a.plusValue(5)}>+5</button>
</>
);
}
action
分为同步 action
和异步 action
,如果 action
中使用了异步函数,则需要通过async
将该 action
声明为异步 action
。
- 同步action
const getStore = createStore({
id: 'user',
state: {
visible: true
},
actions: {
update(state, payload) {
// 更新user信息是同步方法,action为同步action
const data = updateUsersync(payload);
if (data.success) {
// 可以通过this调用该store其他的action
this.hideModal();
}
},
hideModal(state) {
state.visible = false;
},
},
});
- 异步action
const getStore = createStore({
id: 'user',
state: {
visible: true
},
actions: {
async update(state, payload) {
// 更新user信息是异步方法,action为异步action
const data = await updateUser(payload);
if (data.success) {
// 可以通过this调用该store其他的action
this.hideModal();
}
},
hideModal(state) {
state.visible = false;
},
},
});
computed
功能介绍
computed
是一种用来获取 state
状态变量的计算属性,它可以根据依赖关系进行缓存,当它的相关依赖发生改变时才会重新计算。
可通过 $c
访问 Store 对象中的 computed
函数,也可以通过 computed
属性名称访问。两者效果不同,前者获取计算属性函数,后者直接获取计算属性的值。
示例
const getStore = createStore({
id: 'user',
state: {
data: {
list: [
{ id: 1, name: 'liming', age: 18 },
{ id: 2, name: 'xiaohong', age: 20 },
],
},
loading: false,
},
computed: {
users: state => state.data.list,
userCount(states) {
// 通过this获取其他computed的值
return this.users.length;
},
},
});
function User() {
const st = getStore();
return (
<div>
<p>User count is {st.userCount}</p>
{/* 也可以使用 st.$c.userCount() */}
</div>
);
}
$subscribe
功能介绍
使用 $subscribe
方法将监听器 listener
绑定到 Store
上以监听 state
的变化。
接口定义
store.$subscribe(listener: () => void)
示例
const getStore = createStore({
id: 'user',
state: {
data: {
list: [
{ id: 1, name: 'liming', age: 18 },
{ id: 2, name: 'xiaohong', age: 20 },
],
},
loading: false,
},
actions: {
toggleLoading(state) {
state.loading = !state.loading;
},
},
computed: {
users: state => state.data.list,
userCount(states) {
// 通过this获取其他computed的值
return this.users.length;
},
},
});
class User extends Inula.Component {
constructor(props) {
super(props);
this.store = getStore();
}
// 将listener用变量存储起来,用于绑定到store上和从store上解除绑定
listener = () => {
console.log('store changed');
};
// 对于每个User组件中获取到的store对象都应该重新绑定监听器
componentDidMount() {
this.store.$subscribe(this.listener);
}
// 当User组件卸载时,解除store上listener的绑定
componentWillUnmount() {
this.store.$unsubscribe(this.listener);
}
render() {
return (
<div>
<p>loading status is {this.store.loading ? 'true' : 'false'}</p>
<button onClick={() => this.store.toggleLoading()}>Change Loading</button>
{/* 当点击按钮切换 loading 的值时,触发 listener */}
</div>
);
}
}
$unsubscribe
功能介绍
移除 Store
对象上的监听器,如果 listener
绑定在某个函数组件上,那其应在 useEffect
钩子函数中完成,并在返回函数中解除与 listener
的绑定,防止 listener
不断增加。 另外 listener
必须被附加在相同的 Store
实例上。
接口定义
store.$unsubscribe(listener: () => void)
示例
const getStore = createStore({
id: 'user',
state: {
data: {
list: [
{ id: 1, name: 'liming', age: 18 },
{ id: 2, name: 'xiaohong', age: 20 },
],
},
loading: false,
},
actions: {
toggleLoading(state) {
state.loading = !state.loading;
},
},
computed: {
users: state => state.data.list,
userCount(states) {
// 通过this获取其他computed的值
return this.users.length;
},
},
});
function User() {
const store = getStore();
useEffect(() => {
// 将listener用变量存储起来,用于绑定到store上和从store上解除绑定
const listener = () => {
console.log('store changed');
};
// 对于每个User组件中获取到的store都应该重新绑定监听器
store.$subscribe(listener);
// 当User组件卸载时,解除store上listener的绑定
return () => {
store.$unsubscribe(listener);
};
});
return (
<div>
<p>loading status is {store.loading ? 'true' : 'false'}</p>
<button onClick={() => store.toggleLoading()}>Change Loading</button>
{/* 当点击按钮切换 loading 的值时,触发 listener */}
</div>
);
}
$queue[action]
功能介绍
store
对象的 actions
也可以使用 $queue
命名空间来调用,这将把 action
添加到执行队列中。正常情况下 actions
是并行调用的,不需要等待前序 action
完成。当 action
被添加到队列中时,它将等待执行队列前面的所有 action
完成后再执行。
示例
export const getStore = createStore({
state: {
val: 0,
},
actions: {
wait: async () => {
return new Promise(resolve => {
setTimeout(resolve, 2000);
});
},
increment: (state) => {
state.val++;
},
},
});
function SlowComponent() {
const st = getStore();
const onClickEvent = () => {
st.$queue.increment(); // (1) 这个action将会立即执行
st.$queue.wait(); // (2) 这个action将会在(1)完成后执行
st.increment(); // (3) 这个action将会立即执行,与(1)同时开始
st.$queue.increment(); // (4) 这个action将会在(2)完成后执行(2秒后)
};
return (
<div>
<h1>{st.val}</h1>
<button onClick={onClickEvent}>button</button>
</div>
);
}
createStore()
功能介绍
createStore
是 openinula 状态管理器 Inula-X 中用于创建 Store 对象的函数。一个 openinula 应用中可以创建多个 Store
对象,Store
通过 id
来标识不同的对象。
接口定义
function createStore<S, A, C>(config: StoreConfig): () => StoreObj<S, A, C>
type StoreConfig<S, A, C> = {
id?: string;
state?: S;
actions?: A;
computed?: C;
options?: {
isReduxAdapter?: boolean;
};
}
参数说明
config
:一个包含id
,state
,actions
,computed
的 Store 对象,注意state
,actions
,computed
的属性或方法不能重名。
返回值
createStore
返回一个函数,调用该函数得到 StoreObj
对象,StoreObj
对象的结构如下:
type StoreObj<S, A, C> = {
$s: S;
$a: {[K in keyof A]: Action<A[K], S>};
$c: {[K: string]: (state: S) => any}
$queue: {[K in keyof A]: AsyncAction<A[K], S>};
$listeners;
$subscribe: (listener: (mutation) => void) => void;
$unsubscribe: (listener: (mutation) => void) => void;
} & { [K in keyof S]: S[K] } & { [K in keyof A]: Action<A[K], S> } & { [K in keyof C]: ReturnType<C[K]> };
- 可通过
$s
访问 Store 对象中的state
; - 可通过
$a
访问 Store 对象中的actions
; - 可通过
$c
访问 Store 对象中的computed
;
useStore()
功能介绍
useStore
是 openinula 中的一个 Hooks 函数,用于在函数式组件中获取 Store 对象。
接口定义
function useStore<S, A, C>(id: string): StoreObj<S, A, C>
参数说明
id
:要使用的Store对象的 id 值。
clearStore()
功能介绍
从openinula应用中移除对应的 Store 对象。
接口定义
function clearStore(id: string): void
参数说明
id
:要删除的Store对象的id值。