MobX是一款身经百战的状态管理库,它比Redux性能更优秀、功能更强大、使用更灵活、代码量更少!但使用率却不如Redux,我觉得有很大一部分原因是过于灵活,即完成一件事可以有多种方式。这使得不同人编写的MobX风格差异极大!也使得版本升级历史包袱重!MobX6与之前版本有不少差异!
最近我把官方文档从头到尾过了一遍,感觉MobX官方文档真的非常不错,里面有很多状态管理的知识值得细细咀嚼,强烈建议有时间的话还是要亲自啃一遍官方文档。
以下我将以todo案例模拟实战项目,写一篇关于使用typeScript 编写Mobx6的编码规范。在遇到一件事有多种实现方式时我只重点讲一种以及选择它的原因,其他的一笔带过。如有不妥之处,欢迎留言讨论!
如果只写函数组件,建议用mobx-react-lite。如果需要写类组件,建议用mobx-react。
目前我写项目全部用函数组件,拥抱hooks,所以我选择mobx-react-lite。
yarn add mobx mobx-react-lite
import { observer } from "mobx-react-lite"
yarn add mobx mobx-react
import { observer } from "mobx-react"
常见内容有:
常见内容有:
这个要视应用程序的功能而定。
根据用途划分store,建议将强相关的数据集中在一个store中,方便处理。
例如:创建一个todo应用,通常store中要有ui、user、todo这3个store。
不建议像Redux那样所有状态集中在一个store里面。太过深奥的理由不谈,最直接的原因就是开发体验不好。在写redux时会遇到大量类似store.todo.todoitem.name
这样的代码。
以下内容来自官方文档:
import { makeAutoObservable } from "mobx"
export class Todo {
title = ""
finished = false
constructor(title: string) {
makeAutoObservable(this,
{ // 自定义各个类属性的mobx注解,如false(不注解)等
},
{ // options参数,autoBind是指自动绑定this
autoBind: true,
}
)
this.title = title
}
toggle() {
this.finished = !this.finished
}
}
import { makeAutoObservable } from "mobx"
import { Todo } from "./todo"
class TodoList {
todos: Todo[] = []
get unfinishedTodoCount() {
return this.todos.filter((todo) => todo.finished).length
}
constructor(todos: Todo[]) {
makeAutoObservable(this,
{ // 自定义各个类属性的mobx注解,如false(不注解)等
},
{ // options参数,autoBind是指自动绑定this
autoBind: true,
}
)
this.todos = todos
}
add(todo: Todo) {
this.todos.push(todo)
}
}
const todoStore = new TodoList([])
export default todoStore
import { makeAutoObservable } from "mobx"
class User {
name: string
get isLogin() {
return this.name.length>0
}
constructor(name:string) {
makeAutoObservable(this,
{ // 自定义各个类属性的mobx注解,如false(不注解)等
},
{ // options参数,autoBind是指自动绑定this
autoBind: true,
}
)
this.name = name
}
login(name: string) {
this.name = name
}
logout() {
this.name=""
}
}
const userStore = new User("")
export default userStore
import { createContext } from "react"
import todoStore from "./todos"
import userStore from "./user"
// 创建context用来保存各项数据store
export const Store = createContext({ todoStore,userStore })
// StoreProvide组件,用来给子组件传递store
const StoreProvide: React.FC<{
children: React.ReactNode[];
}> = (props) => {
return (
<Store.Provider value={{ todoStore,userStore }} >
{props.children}
</Store.Provider>
)
}
export { Todo } from "./todos/todo";
export default StoreProvide;
import React, { useContext, useRef } from "react"
// import { observer } from "mobx-react"
import { observer } from "mobx-react-lite"
import StoreProvide, { Store, Todo } from './store'
const TodoListView: React.FC = observer(function TodoListView() {
const { todoStore } = useContext(Store)
console.log("渲染了TodoListView")
return (
<div>
<ul>
{todoStore.todos.map((todo, index) => (
<TodoView todo={todo} key={index} />
))}
</ul>
</div>
)
})
const TodoListLeft: React.FC = observer(function TodoListLeft() {
const { todoStore } = useContext(Store)
console.log("渲染了TodoListLeft")
return (
<>
Tasks left: {todoStore.unfinishedTodoCount}
</>
)
})
// const TodoListLeft = observer(
// class TodoListLeft extends React.Component {
// static contextType = Store;
// context!: React.ContextType;
// render() {
// return (
// <>
// Tasks left: {this.context!.todoStore.unfinishedTodoCount}
// >
// )
// }
// })
const AddTodo: React.FC = observer(function AddTodo() {
const { todoStore } = useContext(Store)
console.log("渲染了AddTodo")
const ref = useRef<HTMLInputElement>(null)
return (
<>
<input ref={ref} type="text" />
<button onClick={() => {
const item = new Todo(ref.current!.value)
todoStore.add(item)
ref.current!.value = ""
}} > 新增 </button>
</>
)
})
const TodoView: React.FC<{ todo: Todo }> = observer(function TodoView({ todo }) {
console.log("渲染了TodoView")
return (
<li>
<input
type="checkbox"
checked={todo.finished}
onChange={() => todo.toggle()}
/>
{todo.title}
</li>
)
})
const LoginUser: React.FC = observer(function LoginUser() {
console.log("LoginUser")
const { userStore } = useContext(Store)
const ref = useRef<HTMLInputElement>(null)
return (
<div>
<p>
{userStore.isLogin?`当前登录的用户是:${userStore.name}`:"当前没有登录用户!"}
</p>
<p>
<input ref={ref} type="text" />
<button onClick={() => userStore.login(ref.current!.value)} > 登录 </button>
<button onClick={() => userStore.logout()} > 退出 </button>
</p>
</div>
)
})
const App = () => {
return (
<StoreProvide>
<LoginUser />
<hr/>
<AddTodo />
<TodoListView />
<TodoListLeft />
</StoreProvide>
)
}
export default App
mobx-react
库,函数组件mobx-react
库和mobx-react-lite
两种库都可以使用。有关这2个库的差异在文章第二大段讲过了。const LoginUser: React.FC = observer(function LoginUser() {
,这里的`function LoginUser() {``是为了让组件在React Developer Tools中可以正常显示名字。以下是名字显示正常的截图:const LoginUser: React.FC = observer(()=> {
,组件虽然可以正常工作,但在React Developer Tools中不能正常显示名字。以下是名字显示异常的截图:MobX的内容真的很多,小小的一篇博客没法讲完。以上只是选最常用最基本的内容。后续我还会出一篇有关reactions的博客。
再次建议有时间的朋友一定要细读官方文档,这里不光有很多MobX的使用细则,还有很多怎么做好状态管理的建议。
另外附以上代码的CodeSandbox在线体验。