curTain

使用 mobx 结合 react 搭建的小 demo….

先看效果图:

预览地址:TodoList
github 地址:mobx-react — todoList

1. 前言

本文主要介绍 mobx-react 与 mobx 的使用,最后完成 todolist。

此 demo 全部用 class 组件搭建完成,如需使用 hooks ,请参考mobx与react-hooks

2. 环境准备

需要在mobx—简明学习的基础上。

安装 react一套

yarn add react react-dom prop-types

安装 babel-react相关插件

yarn add @babel/preset-react

安装 mobx-react

yarn add mobx-react

配置 webpack.config.js 文件

配置完成,开始编写文件。

3. mobx-react 说明:

mobx-react 官方文档

可知有如下 api:

  1. observer(componentClass)
  2. Observer —- Observer是一个React组件,它将观察者应用于组件中的匿名区域。
  3. useLocalStore hook
  4. useAsObservableSource hook
  5. Server Side Rendering with useStaticRendering(服务端)
  6. PropTypes
  7. Provider and inject
  8. disposeOnUnmount(componentInstance, propertyKey | function | function[])

可见,有两种 api,一种是针对 componentClass 的,一种是针对 hooks 的。

我们使用 componentClass,所以主要用到:

  1. observer(componentClass) 使类重新render
  2. PropTypes 判定proptype
  3. Provider and inject 提供 store 和 注入对应props

具体的使用,请阅读 官方文档 , 官方文档写得蛮详细的。

4. 开始编写

文件结构:

4.1 store.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import { observable, action, computed, observe } from "mobx"

class Todo {
id = Math.random()
@observable title = ""
@observable finished = false
constructor( title ){
this.title = title
}
@action.bound toggle(){
this.finished = !this.finished
}
}

class Store {
@observable todos = []
disposers = []
@action.bound createTodo( title ){
this.todos.unshift( new Todo( title ) )
}
@computed get left(){
return this.todos.filter( todo => {
return !todo.finished
}).length
}
@action.bound removeTodo( todo ){
this.todos.remove( todo )
}

constructor(){
observe( this.todos, change => {
this.disposers.forEach( disposer => disposer() )
this.disposers = []
for( let todo of change.object ){
var disposer = observe( todo, change => {
console.log( change )
})
this.disposers.push( disposer )
}
})
}
}

const store = new Store()

const ss = {
store
}

export default ss

4.2 index.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import React, { Component } from "react"
import ReactDOM from "react-dom"
import { observer, Provider } from "mobx-react"
import { trace } from "mobx"

import TodoHeader from "./conponent/TodoHeader.jsx"
import TodoView from "./conponent/TodoView.jsx"
import TodoFooter from "./conponent/TodoFooter.jsx"

import ss from "./store.js"

@observer
class TodoList extends Component {
render(){
trace()
return (
<div className="todo-list" >
<TodoHeader />
<ul>
<TodoView />
</ul>
<TodoFooter />
</div>
)
}
}

ReactDOM.render( (
<Provider { ...ss } >
<TodoList />
</Provider>
), document.querySelector("#root") )

4.3 TodoFooter.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React, { Component } from "react"
import { observer, inject } from "mobx-react"
import { trace } from "mobx"

@inject( allStore => {
return {
left: allStore.store.left
}
})
@observer
class TodoFooter extends Component {
render(){
trace()
return (
<footer>
{ this.props.left } items unfinished...
</footer>
)
}
}

export default TodoFooter

4.4 TodoHeader.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import React, { Component } from "react"
import { observer, inject } from "mobx-react"

@inject( "store" )
@observer
class TodoHeader extends Component {
state = {
inputValue : ""
}

handleSubmit = ( e ) => {
e.preventDefault()

let store = this.props.store
var inputValue = this.state.inputValue

store.createTodo( inputValue )

this.setState({ inputValue: "" })

}
handleChange = ( e ) => {
e.persist()
var inputValue = e.target.value
this.setState({
inputValue: inputValue
})
}

render(){
return (
<header>
<form onSubmit={ this.handleSubmit } >
<input
type="text"
onChange={ e => this.handleChange( e )}
value={ this.state.inputValue }
className="input"
placeholder=" what deeds to be finished? "
/>
</form>
</header>
)
}
}

export default TodoHeader

4.5 TodoView.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import React, { Component, Fragment } from "react"
import PropTypes from "prop-types"
import { observer, inject } from "mobx-react"

@inject( "store" )
@observer
class TodoView extends Component {
render(){
const store = this.props.store
const { todos } = store
return todos.map( todo => {
return (
<li key={ todo.id } className="todo-item">
<TodoItem todo={ todo } />
<span className="delete" onClick={ e => store.removeTodo( todo ) } >X</span>
</li>
)
})
}
}

@observer
class TodoItem extends Component{
static propTypes = {
todo: PropTypes.shape({
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
finished: PropTypes.bool.isRequired
}).isRequired
}
handleClick = () => {
this.props.todo.toggle()
}
render(){
const todo = this.props.todo
return (
<Fragment>
<input
type="checkbox"
className="toggle"
checked={ todo.finished }
onChange={ this.handleClick } />
<span className={["title", todo.finished && "finished"].join(" ")} >
{ todo.title }
</span>
</Fragment>
)
}
}

export default TodoView

4.6 html 文件内添加 css 样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
.input {
padding: 15px;
border: 1px solid #ccc;
font-size: 24px;
width: 400px;
}
.todo-item {
display: flex;
align-items: center;
height: 40px;
border-bottom: 1px solid #ccc;
}
.todo-item .toggle {
margin-right: 20px;
}
.todo-item .title {
font-size: 24px;
color: #000;
}
.todo-item .title.finished {
text-decoration: line-through;
color: #ccc;
}
.todo-item .delete {
margin-left: 20px;
cursor: pointer;
}
footer{
font-size: 22px;
}

5. 性能优化

提升性能三大法则:

  1. 细粒度拆分视图组件
  2. 使用专用组件处理列表
  3. 尽可能晚的结构可观察数据

上面三大法则,你在上面的代码中,能找到在哪里吗??

还有其他的优化点:

  1. 尽早的绑定函数(不要传匿名函数和箭头函数)
  2. 不要使用数组的索引作为 key
  3. 使用小组件

详情参考:优化 React 组件渲染

6. 总结

当得到一些点拨过后,再去阅读官方文档,就会收获更多,

当然也要学会阅读官方文档,官方文档写得都蛮好的。

写下这篇文章,也算是总结了一下 mobx 相关的知识.

加油,写下这篇文章希望对你有帮助,与君共勉!!

有时间,我会把 class 版本改成 hook 版本的。

7. 参考材料

茵风泳月–mobx入门基础教程

mobx—-官网

mobx-react 文档

mobx-react 官方文档


 评论