跳到主要内容

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

stateStore 中存储数据的部分,通常把组件间需要共享的状态存放在 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

actionsstate 中处理数据逻辑的部分,对 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值。

欢迎关注openInula微信公众号