场景
在 mobx
中,使用 @observable
定义的 state
,修改时需要借助 @action
包装才能修改,在变量比较多的情况下,十分不便,这时可以封装一个类似 React 里的 setState
方法,统一修改,在结合 TypeScript
类型推导,可以实现代码联想功能。
使用 TS 遍历指定对象,并形成一个新的类型声明
由于 Mobx Store
本质是一个 object
对象,所以需要声明一个可以遍历某个对象的方法,这里还需要排除 function
,我只是需要赋值 mobx 管理的 state 即可,具体代码如下:
type GetStoreState<T extends Record<string, any>> = {
[Key in keyof T as T[Key] extends Function ? never : Key]: T[Key];
};
以上代码分解:
- 使用 type 声明一个 TS 工具方法,接收一个泛型
T extends Record<string, any>
,并为泛型缩窄类型,指定类型为可索引类型
; [Key in keyof T as T[Key] extends Function ? never : Key]
分两步理解:Key in keyof T
代表 key 属于传入对象的某一个属性;as T[Key] extends Function ? never : Key
代表一个 js 里的三元表达式,T[Key] extends Function
代表当前遍历的属性值如果为function
则返回never
,否则返回当前遍历的 key;
T[Key]
代表遍历当前对象的属性值。
具体使用场景示例
完整代码展示
import { action, observable } from 'mobx';
type MenuItemType = {
id: string;
content: string;
};
export type GetStoreState\<T extends Record\<string, any\>\> = {
\[Key in keyof T as T\[Key\] extends Function ? never : Key\]: T\[Key\];
};
type State = Partial\<GetStoreState\<BaseStore\>\>;
class BaseStore {
/\*\* 标题 \*/
@observable title = '';
/\*\* 副标题 \*/
@observable subTitle = '';
/\*\* 菜单列表 \*/
@observable menuList: MenuItemType\[\] = \[\];
/\*\*
* 统一更新数据
* @param params 需要更新的数据
* @param callback 更新后的回调方法
*/
@action
setState = (params: State, callback?: () => void) => {
Object.keys(params).forEach((key) => {
if (key in this) {
(this[key as keyof this] as unknown) = params[key as keyof State];
}
});
callback?.();
};
/\*\*
* 示例方法
*/
init = () => {
this.setState({
title: '我是标题',
});
};
}
`export default BaseStore;`
扩展使用
一个项目中,会存在很多个 store,避免代码冗余,可以将此 store 封装,做基础 store,其他使用场景,统一继承,具体实现:
class BaseStore<State extends Record<string, any>> {
/**
* 统一更新数据
* @param params 需要更新的数据
* @param callback 更新后的回调方法
*/
setState = (params: GetStoreState<Partial<State>>, callback?: () => void) => {
Object.keys(params).forEach((key) => {
if (key in this) {
(this[key as keyof this] as unknown) =
params[key as keyof GetStoreState<Partial<State>>];
}
});
callback?.();
};
}
class ExampleStore extends BaseStore\<ExampleStore\> {
pageInfo: Record\<string, any\> = {};
`init = () => {
this.setState({
pageInfo: {
title: 2,
},
});
};
}`