本周对 mobx 进行了学习,写了一个 tosolist demo 练练手~(没写完)
一、Mobx
1.1 介绍
- mobx 是一个简单的可拓展的状态管理库,无样本代码风格简约
- 不推荐使用装饰器语法
- 可以运行在任何支持es5的环境中,包含浏览器和 node
1.2 核心
1.2.1 observable
- 被 mobx 跟踪的状态
1.2.2 action
- 通过某个方法去修改状态的话,这个方法必须被标记为 action 方法
- 只有 action 方法才能修改状态
- 只有修改状态之后这个视图才进行更新
1.2.3 computed
-
派生状态
-
根据现有状态衍生出来的状态
1.2.4 flow
- 用来标识方法
- 想要应用当中执行副作用,副作用执行完成之后要更改状态
- 这个方法必须被标志为 flow
1.3 使用
- mobx:核心库
- mobx-react-lite:适用于函数组件
- mobx-react:适用于函数组件和类组件
- 安装:
pnpm i mobx mobx-react-lite
二、功能需求
- 实现对任务列表的增删改查
三、实现过程
需安装:
发送网络请求:
pnpm i axios
模拟返回后端数据:
pnpm i json-server
编写样式:
pnpm i styled-components
3.1 初始化
-
任务对象和任务列表都是状态,因此两者都是使用类进行管理
-
store
-
modules
- todo类
import { makeObservable, observable } from "mobx"
export default class Todo{
constructor(todo) {
this.id = todo.id
// 状态
this.title = todo.title
this.isCompleted = todo.isCompleted || false
this.isEditing = false
makeObservable(this, {
title: observable,
isCompleted: observable,
isEditing:observable
})
}
}
- todoStore.js
import { makeObservable, observable } from "mobx"
export default class TodoStore{
constructor() {
this.todoList=[]
makeObservable(this, {
todoList:observable,
// 更正 this 指向
addTodo: action.bound,
})
}
// 增加任务
addTodo(title) {
// 将任务(title、id)加入`任务列表`中:通过 todo 类生成一个实例任务
this.todoList.push(new Todo({ title, id: this.createId() }));
}
// 创建 id
createId() {
// 数组中还没有任务,id 为 1
if (!this.todoList.length) return 1;
// 通过 reduce 方法进行遍历
return this.todoList.reduce((id, todo) => (id < todo.id ? todo.id : id), 0) + 1;
// return this.todoList.length + 1;
}
}
- index.js
import { createContext, useContext } from "react";
import CounterStore from "./CounterStore";
import TodoStore from "./TodoStore";
class RootStore {
constructor() {
this.counterStore = new CounterStore();
this.todoStore = new TodoStore()
}
}
// 创建一个 rootStore 实例
const rootStore = new RootStore();
// 创建一个上下文对象
const RootStoreContext = createContext();
// 给下层组件提供 rootStore
export const RootStoreProvider = ({ children }) => {
return <RootStoreContext.Provider value={rootStore}>{children}</RootStoreContext.Provider>;
};
// 导出 RootStore 对象
export const useRootStore = () => {
return useContext(RootStoreContext)
}
3.2 添加任务
- todoStore 增加状态
import { makeObservable, observable } from "mobx"
export default class TodoStore{
constructor() {
this.todoList=[]
makeObservable(this, {
todoList:observable,
// 更正 this 指向
addTodo: action.bound,
})
}
// 增加任务
addTodo(title) {
// 将任务(title、id)加入`任务列表`中:通过 todo 类生成一个实例任务
this.todoList.push(new Todo({ title, id: this.createId() }));
}
// 创建 id
createId() {
// 数组中还没有任务,id 为 1
if (!this.todoList.length) return 1;
// 通过 reduce 方法进行遍历,往最大 id 加 1
return this.todoList.reduce((id, todo) => (id < todo.id ? todo.id : id), 0) + 1;
}
}
- 操作任务列表
import React, { memo, useState } from "react";
import { useRootStore } from "../../store";
import { HeaderWrapper } from "./style";
const TodoHeader = memo(() => {
const [title, setTitle] = useState("");
const { todoStore } = useRootStore();
const { addTodo } = todoStore;
return (
<HeaderWrapper>
<header className='header'>
<input
className='input'
type='text'
placeholder='what needs to be done'
autoFocus
value={title}
onChange={(e) => setTitle(e.target.value)}
onKeyUp={(e) => {
if (e.key !== "Enter") return;
// 增加任务
addTodo(title);
setTitle("");
}}
/>
</header>
</HeaderWrapper>
);
});
export default TodoHeader;
3.3 展示任务列表
- TodoMain
import React from "react";
import { observer } from "mobx-react-lite";
import { useRootStore } from "../../store";
import TodoItem from "../TodoItem";
import { MainWrapper } from "./style";
const TodoMain = observer(() => {
const { todoStore } = useRootStore();
const { todoList } = todoStore;
return (
<MainWrapper>
{todoList.map((item) => (
<TodoItem todo={item} key={item.id} />
))}
</MainWrapper>
);
});
export default TodoMain;
- TodoItem
import { observer } from "mobx-react-lite";
import React from "react";
import { ItemWrapper } from "./style";
const TodoItem = observer((props) => {
const { todo } = props;
return (
<ItemWrapper>
任务id:{todo.id} 任务名称:{todo.title}
<span className="deleteBtn">delete</span>
</ItemWrapper>
);
});
export default TodoItem;
3.4 加载远端任务
- 开启本地服务器,配置 script
"json-server": "json-server --watch ./src/data/db.json --port 3001"
- 使用 flow 标识,获取到任务之后添加到任务列表
import { action, flow, makeObservable, observable } from "mobx";
import Todo from "./Todo";
import axios from "axios";
// 存储一个列表状态
export default class TodoStore {
constructor() {
this.todoList = [];
// 标识状态
makeObservable(this, {
loadTodoList: flow,
});
this.loadTodoList()
}
// 模拟获取服务器端任务
async loadTodoList() {
const res = await axios.get("http://localhost:3001/todos");
res.data.forEach((todo) => {
this.todoList.push(new Todo(todo));
});
}
}
3.5 更改任务是否已完成状态
- 添加 input 输入框,设置
type='checkbox'
import { observer } from "mobx-react-lite";
import React from "react";
import { ItemWrapper } from "./style";
const TodoItem = observer((props) => {
const { todo, isCompleted, modifyTodoIsComputed } = props;
return (
<ItemWrapper>
<input type='checkbox' className='toggle' checked={isCompleted} onChange={modifyTodoIsComputed} />
任务id:{todo.id} 任务名称:<span className='title'>{todo.title}</span>
<span className='deleteBtn'>delete</span>
</ItemWrapper>
);
});
export default TodoItem;
- 改变的是任务的状态,来到 Todo 类
import { action, makeObservable, observable } from "mobx"
// 创建任务列表类
export default class Todo{
constructor(todo) {
// 属性:id title isCompleted isEditing
this.id = todo.id
// 状态
this.title = todo.title
this.isCompleted = todo.isCompleted || false
this.isEditing = false
// 标识状态
makeObservable(this, {
title: observable,
isCompleted: observable,
isEditing: observable,
modifyTodoIsComputed:action.bound
})
}
// 更改任务是否已完成状态
modifyTodoIsComputed() {
this.isCompleted = !this.isCompleted
}
}
3.6 删除任务
- 根据 id 进行删除
// 根据 id 删除任务
removeTodo(id) {
console.log(id);
this.todoList = this.todoList.filter((todo) => todo.id !== id);
}
- 点击删除按钮调用删除方法
import { observer } from "mobx-react-lite";
import React from "react";
import { ItemWrapper } from "./style";
import { useRootStore} from '../../store/index'
const TodoItem = observer((props) => {
const { todo} = props;
const { isCompleted, modifyTodoIsComputed } = todo
const { todoStore } = useRootStore()
const {removeTodo} = todoStore
return (
<ItemWrapper>
<input type='checkbox' className='toggle' checked={isCompleted} onChange={modifyTodoIsComputed} />
任务id:{todo.id} 任务名称:<span className='title'>{todo.title}</span>
<span className='deleteBtn' onClick={() => removeTodo(todo.id)}>
delete
</span>
</ItemWrapper>
);
});
export default TodoItem;