Mobx, 一个简单、可扩展的状态管理插件, 可结合React, 小程序使用。
这篇文章主要讲一下mobx的基本使用,并且结合React设计一个简单的store。
下面简单介绍一下在react中使用mobx需要掌握的两个简单的概念。
- Observable state(可观察的状态)
Mobx可以让现有的js数据结构(数组,对象)添加可观察功能。
这个功能主要是指,能对数据的各种操作进行监听(例如数组的元素的变化,对象的属性的值的更改。),从而实现现下前端所流行的响应式编程。
- Observer (观察者)
mobx-react这个库,提供一个叫observer的功能。它能使react组件变成观察者模式。
在观察者模式下,它具有监测Observable state的功能。
当Observable state发生变化时,观察者会收到信息,然后响应这个变化作出事先定义好的行为。
以react来说,它在收到Observable state发生变化的信息时,它会重新调用render方法,把数据更新到视图上。
下面举一个简单的例子
class Counter extends Component {
count = 0
render() {
return (
<div>
Counter: {this.count}
<Button onClick={this.handleAdd}>+</Button>
<Button onClick={this.handleDec}>-</Button>
</div>
)
}
handleAdd = () => {
this.count++
}
handleDec = () => {
this.count--
}
}
这是一个简单的计数器的例子。通过实例变量count保存数字,通过handleAdd和handleDec这两个方法可以改变count的值。然后在react组件的render函数里把count这个变量映射到视图上。
咋看一下好像没什么问题,但其实有使用react经验的都知道,在react里更新视图需要state属性和setState方法来配合才有意义。即使是redux这个库,也不过是在组件最顶层管理state,然后把state分发到各个组件来使用。
但是mobx能使上面的这种结构变为可能。
@observer
class Counter extends Component {
@observable count = 0
render() {
return (
<div>
Counter: {this.count}
<Button onClick={this.handleAdd}>+</Button>
<Button onClick={this.handleDec}>-</Button>
</div>
)
}
handleAdd = () => {
this.count++
}
handleDec = () => {
this.count--
}
}
observable这个方法会让count变成可观察的state。而observer这个方法会使react组件变成观察者。当count发生变化时,身为观察者的react组件会对应这个变化而重新调用render方法来更新视图。
PS
Observable state 和 Observer 是互相独立的。
可观察状态不一定是观察者自身的属性。Observable state可以独立于组件外,单独存在于一个对象里。
创建一个给React组件使用的store
下面会以还原真实情景为目标,设计一个存储后台api数据的store。
- 为了数据更真实,以下提供一个真实的api地址。
CNode社区api目录,这是node中文社区网站对外开放的一个api目录。CNode的数据主要以帖子为主。以下会选择主题详情这个api来做讲解
这是这个api返回的数据的基本结构
{
"success": true,
"data": {
"id": "5433d5e4e737cbe96dcef312",
"author_id": "504c28a2e2b845157708cb61",
"tab": "share",
"title": "一个面向 Node.js 初学者的系列课程:node-lessons",
"last_reply_at": "2016-06-16T08:12:21.234Z",
"good": true,
"top": false,
"reply_count": 85,
"visit_count": 28422,
"create_at": "2014-10-07T12:00:36.270Z",
"is_collect": false,
"author": {
"loginname": "alsotang",
"avatar_url": "https://avatars2.githubusercontent.com/u/1147375?v=3&s=120"
},
"replies": []
}
}
-
现在开始设计存储主题详情的store。
为了让数据结构更清晰,会采用class的形式来构造。
api返回的数据会以一个实例变量(state)来接受。
并且state会被构造为Observable stateclass TopicStore { @observable state = { isFetching: false, isRejected: false, isFulfilled: false, success: false, data: { // "id": "5433d5e4e737cbe96dcef312", // "author_id": "504c28a2e2b845157708cb61", // "tab": "share", // "title": "一个面向 Node.js 初学者的系列课程:node-lessons", // ........ } } }
从上面的例子可以看到,state除了保存了api的返回结果以外,还具有3个bool属性来表示状态。
- isFetching 用于存储api请求的状态。
- isRejected 用于表示错误状态。
- isFulfilled 用于表达数据充满后的状态。
定义了存储数据的结构后,还需要一个获取数据的方法。
class TopicStore { @observable state = { isFetching: false, isRejected: false, isFulfilled: false, success: false, data: { // "id": "5433d5e4e737cbe96dcef312", // "author_id": "504c28a2e2b845157708cb61", // "tab": "share", // "title": "一个面向 Node.js 初学者的系列课程:node-lessons", // ........ } } // 这是一个请求主题详情的方法 fetchTopic(id) { // 在api请求前,先需要保存请求的状态(把状态置为请求中) this.state.isFetching = true // 然后发请求 fetch(...) .then(res => { // 在这里处理成功后的状态。 // 假设res就是api返回的数据,数据格式参考上面。 // 把返回的数据合并到state上 Object.assign(this.state, res) // 然后还需要把状态置为请求成功。 this.state.isFetching = false this.state.isFulfilled = true //上面可以简写成 Object.assign(this.state, res, { isFetching: false, isFulfilled: true }) }) .catch( // 错误处理。这里不做介绍。 ) } }
最后创建store的实例,以供react组件使用。
export const topicStore = new TopicStore()
最后需要创建一个react组件来展示主题详情的数据。
import { topicStore } // 导入store实例
@observer //observer让组件变成观察者模式
export default class TopicPage extends Component {
componentDidMount() {
// 在组件的生命周期内获取数据。
topicStore.fetch()
}
render() {
// 在render里使用store里的数据。
const { id, title, tab, ...data } = topicStore.state.data
renturn ...
}
}
至此,一个简单的Store就构造完成了。