Mobx 基本入门

  • June 06, 2017 09:30
  • Posted by jamie
  • 0 comments

Mobx, 一个简单、可扩展的状态管理插件, 可结合React, 小程序使用。

这篇文章主要讲一下mobx的基本使用,并且结合React设计一个简单的store。

下面简单介绍一下在react中使用mobx需要掌握的两个简单的概念。

  1. Observable state(可观察的状态)

    Mobx可以让现有的js数据结构(数组,对象)添加可观察功能。 这个功能主要是指,能对数据的各种操作进行监听(例如数组的元素的变化,对象的属性的值的更改。),从而实现现下前端所流行的响应式编程。

  2. 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。

  1. 为了数据更真实,以下提供一个真实的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": []
       }
    }
    
  2. 现在开始设计存储主题详情的store。

    为了让数据结构更清晰,会采用class的形式来构造。 api返回的数据会以一个实例变量(state)来接受。 并且state会被构造为Observable state

    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",
          // ........
        }
      }
    }
    

    从上面的例子可以看到,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()
    
  3. 最后需要创建一个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就构造完成了。

Comments