用于合并多个mobx的store工具函数
关键点:
使用 Object.defineProperty 定义 getter
懒加载:只有第一次访问 store.user 时,才会 new UserStore()
单例:之后每次访问都返回同一个实例
js
// 用来合并多个store
export interface StoreConstructor<T> {
new (): T;
}
export type InstanceForEach<T extends Record<string, StoreConstructor<any>>> = {
[P in keyof T]: InstanceType<T[P]>;
};
export function lazyCreateStore<U, T>(
store: U,
key: keyof U,
ctr: StoreConstructor<T>
) {
let _instance: T = null as T;
Object.defineProperty(store, key, {
configurable: false,
enumerable: true,
get() {
if (!_instance) {
_instance = new ctr();
}
return _instance;
},
set() {
/** noop */
},
});
}
export const createStore = <T extends Record<string, StoreConstructor<any>>>(
storeClasses: T
) => {
const _store: InstanceForEach<T> = {} as InstanceForEach<T>;
Object.keys(storeClasses).forEach((key) => {
lazyCreateStore(_store, key as keyof T, storeClasses[key]);
});
(window as any).store = _store;
return _store;
};
创建两个store
js
//count.ts
import { makeObservable, observable, action } from "mobx";
export class CountStore {
num = 0;
constructor() {
makeObservable(this, {
num: observable,
add: action,
});
}
add = () => {
this.num++;
};
}
js
// todoStore.ts
import { makeObservable, observable, action, computed } from "mobx";
export type Todo = {
task: string;
completed: boolean;
assignee: null | string;
};
export type TodoList = Todo[];
export class TodoStore {
todoList: TodoList = [
{
task: "喝水",
completed: false,
assignee: null,
},
];
constructor() {
makeObservable(this, {
todoList: observable,
addTodo: action,
editTodo: action,
completedTodoCount: computed,
report: computed,
});
}
addTodo = (task: string) => {
this.todoList.push({
task,
completed: false,
assignee: null,
});
};
// 返回完成的任务数
get completedTodoCount() {
return this.todoList.filter((todo) => todo.completed === true).length;
}
editTodo = (index: number, todo: Todo) => {
this.todoList[index] = todo;
};
get report() {
if (this.todoList.length === 0) {
return "无";
}
const nextTodo = this.todoList.find((todo) => todo.completed === false);
return `下一个待办"${nextTodo?.task}"。进度${this.completedTodoCount}/${this.todoList.length}`;
}
}
导出
js
// store/index.ts
import { createStore } from "./util";
import { CountStore } from "./Count";
import { TodoStore } from "./TodoStore";
const storeClasses = {
count: CountStore,
todo: TodoStore,
};
export const store = createStore(storeClasses);
页面上使用
js
import { observer } from "../which";
import { store } from "../store";
import { Todo } from "../store/TodoStore";
const TodoListPage = observer(() => {
const addNewTodo = () => {
store.todo.addTodo(prompt("输入新的待办任务", "来杯水"));
};
return (
<div>
<h1>TodoListPage</h1>
<button onClick={addNewTodo}>新增任务</button>
<p>完成了{store.todo.completedTodoCount}个任务</p>
<p>{store.todo.report}</p>
<ul>
{store.todo.todoList.map((todo, index) => (
<TodoView key={index} todo={todo} index={index} />
))}
</ul>
</div>
);
});
const TodoView = observer(({ todo, index }: { todo: Todo; index: number }) => {
return (
<li
className="card"
onDoubleClick={() =>
store.todo.editTodo(index, {
...todo,
task: prompt("任务名称", todo.task) || todo.task,
})
}
>
<input
type="checkbox"
checked={todo.completed}
onChange={() =>
store.todo.editTodo(index, { ...todo, completed: !todo.completed })
}
/>
{todo.task}
</li>
);
});
export default TodoListPage;
mobx源码实现 todo...
- 首先函数组件用到的包
mobx 和 mobx-react-lite(专注于函数组件,体积较小)
常见api
js
import {
makeAutoObservable,
makeObservable,
observable,
action,
computed,
Reaction,
AnnotationsMap,
runInAction,
autorun,
Provider,
inject,
} from "mobx";
runInAction:用来在异步操作后修改状态。
autorun:监听状态变化并自动执行副作用 类似于vue中的watch