Mobx状态管理,如何实现 Mobx + React 版本的 todoList ?
发布于 作者:苏南大叔 来源:程序如此灵动~还是接着分析mobx
官方教程,本文描述官方的React
版本的todoList
。那么,mobx
是如何驱动react
组件里面的数据的呢?如何让每个引用都变得Reactive
的呢?这就是本文要讨论的问题。
苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0
,create-react-app@5.0.1
,react@18.3.1
,mobx@6.13.5
。当react
碰到了mobx
,就产生了本文的写在背景信息。
前文回顾
普通非React
版本的todoList
,Mobx
官方教程的修改版,参考如下:
本文的例子中,使用props
传递store
,使用了useCallback
包装传递函数。参考文章:
Store
这个Store.js
和原来没有React
的时候,代码基本一致,其实就是一个被Mobx
观察过的普通类。基于官方的代码,略有改动。
import { makeObservable, observable, computed, action, autorun } from "mobx";
class TodoStore {
todos = [];
pendingRequests = 0;
constructor() {
makeObservable(this, {
todos: observable,
pendingRequests: observable,
completedTodosCount: computed,
report: computed,
addTodo: action,
addTodoSync: action,
modify: action,
});
// 这是另外一个函数包装 autorun()
autorun(() => console.log(this.report));
}
get completedTodosCount() {
return this.todos.filter((todo) => todo.completed === true).length;
}
get report() {
if (this.todos.length === 0) return "";
const nextTodo = this.todos.find((todo) => todo.completed === false);
const percent = `进度:${this.completedTodosCount}/${this.todos.length}`;
return percent + `,准备:${nextTodo ? nextTodo.name : "<无>"}`;
}
addTodo(name) {
if (!name || name.trim() === "") {
return;
}
this.todos.push({
name,
completed: false,
});
}
addTodoSync(name, timeout = 1000) {
if (!name || name.trim() === "") {
return;
}
this.pendingRequests++;
let that = this;
setTimeout(
action(() => {
that.addTodo(name);
that.pendingRequests--;
}),timeout
);
}
modify(id, info) {
this.todos[id] = { ...this.todos[id], ...info };
}
}
window.store = new TodoStore();
export default window.store;
这里把store
挂在window.
上的主要目的,还是想留着直接在f12
里面调试。
这个代码中,增加了两个action
,
- 一个是对官方的额外远程加载
todo
的代码封装addTodoSync()
。 - 另外一个是对
todo
自身字段修改的函数modify()
。
布局相关逻辑
App.js
:
import { observer } from "mobx-react";
import React, { useCallback } from "react";
import "./App.css";
import RenderCount from "./RenderCount";
import store from "./Store";
const TodoItem = observer(({ modify, id, todo }) => {
console.log("item", id, todo);
const onToggleCompleted = () => {
modify(id, { completed: !todo.completed });
};
const onRename = () => {
modify(id, { name: prompt("任务名称", todo.name) || todo.name });
};
return (
<li onDoubleClick={onRename}>
<input
type="checkbox"
checked={todo.completed}
onChange={onToggleCompleted}
/>
{todo.name}
<RenderCount />
</li>
);
});
const TodoLayout = observer(({ store }) => {
const onNewTodo = () => {
let tmp = (Math.random() * 100).toFixed(0);
console.log(tmp, tmp % 2);
if (tmp % 2 == 0) {
store.addTodo(prompt("输入新的待办:", "请来杯咖啡"));
} else {
store.addTodoSync(prompt("输入新的待办:", "请来瓶红牛"));
}
};
const fn = useCallback((id, info) => store.modify(id, info), []);
// const fn = (id, info) => store.modify(id, info);
return (
<div>
{store.report}
<ul>
{store.todos.map((todo, id) => (
<TodoItem key={id} modify={fn} id={id} todo={todo} />
))}
</ul>
{store.pendingRequests > 0 ? <marquee>正在加载……</marquee> : null}
<button onClick={onNewTodo}>新待办</button>
<small>(双击待办进行编辑)</small>
</div>
);
});
const App = () => {
return (
<>
<TodoLayout store={store}></TodoLayout>
</>
);
};
export default App;
这里调用了RenderCount
组件,参考前文:
这部分代码相比较官方代码,也做了一些修改。这里想说的是:react
循环一个子组件的时候,添加的key
是无法被子组件的props
所获取的。
高阶组件 observer()
这里带来了一个新的概念,高阶组件observer()
,它来自mobx-react
或者 mobx-react-lite
。
使用方式是:通过observer()
包裹一个React
组件。用于将React
组件连接到MobX
状态树,将React
组件转化为响应式组件。
调用方式如下:
import { observer } from "mobx-react";
因此,需要安装两个第三方库了。
npm install mobx mobx-react --save
或者
npm install mobx mobx-react-lite --save
在mobx-react
和mobx-react-lite
中都有observer
,后者的observer
不支持将类组件转换成响应式组件,但是拥有更小的体积。
结语
更多mobx
相关文章,可以参考苏南大叔的博客:
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。