5 React项目架构 - Store

 

相关commit

什么是Store

store - 数据流解决方案

Mobx - 一种flux模式下,数据流解决方案的后起之秀


代码实现

.babelrc

{
  "presets": [
    "stage-1",
    ...
    ],
  "plugins": [
    "transform-decorators-legacy",
    ...
  ]
}

说明:


测试mobx

client/store/app-state.js

import {
  observable,
  computed,
  action,
  autorun,
} from "mobx"

class AppState {
  @observable count = 0

  @observable name = 'Azen' // #2

  @computed get msg() {     // #3
    return `${this.name} say count is ${this.count}`    // #4
  }

  @action add() {           // #7
    this.count += 1
  }

  @action changeName(name) {
    this.name = name
  }
}

const appState = new AppState()

autorun(() => {     // #5
  console.log(appState.msg)
})

setInterval(() => { // #6
  appState.add()
})

export { appState as default, AppState }    // #8

说明:

  1. 使用computed等Mobx相关特性的时候,使用class去组织最方便,可以通过this调用相关功能
  2. 使用装饰器指定值
  3. 计算属性,可以使用点语法调用
  4. 使用模板语句拼句子
  5. autorun:文档mobx内置方法,当AppState更新,会自动调用
  6. setInterval:js内置方法,每秒调用一次,用于测试
  7. 声明action:需要保证只有action能更新state
  8. AppState在propTypes里会用到,也需要导出下


app.js

import { Provider } from 'mobx-react'
import appState from './store/app-state'

const render = (Component) => {
  ReactDOM.hydrate(
    <AppContainer>
      <Provider appState={appState}> // #1
        <BrowserRouter>
          <Component />
        </BrowserRouter>
      </Provider>
    </AppContainer>, root,
  )
};

说明:

  1. 需要用Provider给入口包一层,把定义好的appState传给它
    • 包一层:
      • 原理是使用了隐藏的react api,名字叫context
      • 让内容从顶层传到下面任何一层组件,任何一层都可以拿到


使用Store

client/views/topic-list/index.jsx

import React from 'react'
import {
  observer,
  inject,
} from 'mobx-react'
import PropTypes from 'prop-types'  // #2
import { AppState } from '../../store/app-state'

@inject('appState') @observer   // #1
class TopicList extends React.Component {
  constructor() {
    super()
    this.changeName = this.changeName.bind(this)    // #4
  }

  componentDidMount() {
    //  placeholder
  }

  changeName(event) {
    this.props.appState.changeName(event.target.value)
  }

  render() {
    return (
      <div>
        <div>ㄟ...这里是to...topic li..li..list(。•́-ก̀。)</div>
        <br />
        <input type="text" onChange={this.changeName} />
        <span>{this.props.appState.msg}</span>
      </div>
    )
  }
}

TopicList.propTypes = {
  // appState: PropTypes.object.isRequired  // #3
  appState: PropTypes.instanceOf(AppState).isRequired,
}

export default TopicList

说明:

  1. @inject:app.js中包的Provider提供的属性名字
    1. @observer:声明这个组件是observable的
  2. 声明props类型
    1. 安装`npm i prop-types -S`
  3. airbnb的eslint规则,不让把类型设置为objecte类型,因为js里所有东西都是object
  4. 需要绑定的原因:在我们执行onChange的时候,上下文已经不在组件内部了,但是我们需要用到this,所以就通过bind的方式,让执行changeName的时候可以调用this