Mobx入门之二:asynchronous actions

这一节主要看mobx怎么实现asynchronous actions

1 要实现的demo功能

输入地名,查询天气,利用openweathermap api

26f70e4d53945c6fe2f71132469b4c65e37.jpg

2 思想

observable观察数据:location地点、temperature温度

observer响应式组件:

3d5dbbbc56b59113dd4a58e13bf8e6027ca.jpg

actions: location输入更新、点击add异步查询天气

3 代码块

App:Container组件

// App
const App = observer(({ temperatures }) => (
  <ul>
    <TemperatureInput temperatures={temperatures} />
    {temperatures.map(t =>
      <TView key={t.id} temperature={t} />
    )}
    <DevTools/>
  </ul>
))

ReactDOM.render(
  <App temperatures={temps}/>,
  document.getElementById('root')
)

 

TemperatureInput:地址输入Container组件

@observer
class TemperatureInput extends React.Component {
  @observable input = ''
  render () {
    return (
      <li>
        Destination
        <input value={this.input}
               onChange={this.onChange}/>
        <button onClick={this.onSubmit}>Add</button>
      </li>
    )
  }
  @action onChange = e => {
    this.input = e.target.value
  }

  @action onSubmit = () => {
    this.props.temperatures.push(new Temperature(this.input))
    this.input = ''
  }
}

 

TView:地点天气列表Container组件

@observer
class TView extends React.Component {
  render () {
    const t = this.props.temperature
    return (
      <li key={t.id} onClick={() => this.onTemperatureClick()}>{t.location}: {t.loading ? 'loading...' : t.temperature}</li>
    )
  }

  @action onTemperatureClick = () => {
    this.props.temperature.inc()
  }
}

 

Temperature: Data Model组件

class Temperature {
  id = Math.random()
  @observable unit = 'C'
  @observable temperatureCelsius = 25
  @observable location = 'Amsterdam, NL'
  @observable loading = true

  constructor (location) {
    this.location = location
    this.fetch()
  }
  @computed get temperatureKelvin () {
    console.log('calculating Kelvin')
    return this.temperatureCelsius * (9 / 5) + 32
  }

  @computed get temperatureFahrenheit () {
    console.log('calculating Fahrenheit')
    return this.temperatureCelsius + 273.15
  }

  @computed get temperature () {
    console.log('calculating temperature')
    switch (this.unit) {
      case 'K':
        return this.temperatureKelvin + '°K'
      case 'F':
        return this.temperatureFahrenheit + '°F'
      case 'C':
        return this.temperatureCelsius + '°C'
      default:
        return this.temperatureCelsius + '°C'
    }
  }

  @action fetch () {
    window.fetch(`http://api.openweathermap.org/data/2.5/weather?appid=${APPID}&q=${this.location}`)
      .then(res => res.json())
      .then(action(json => {
        this.temperatureCelsius = json.main.temp - 273.15
        this.loading = false
      }))
  }
  @action setUnit (newUnit) {
    this.unit = newUnit
  }

  @action setCelsius (degrees) {
    this.temperatureCelsius = degrees
  }

  @action('update temperature and unit')
  setTemperatureAndUnit (degrees, unit) {
    this.setUnit(unit)
    this.setCelsius(degrees)
  }

  @action inc() {
    this.setCelsius(this.temperatureCelsius + 1)
  }
}

4 回顾

action:动作是任何用来修改状态的东西;只会对当前运行的函数作出反应,而不会对当前运行函数所调用的函数(不包含在当前函数之内)作出反应,这意味着如果 action 中存在 setTimeout、promise 的 then 或 async 语句,并且在回调函数中某些状态改变了,那么这些回调函数也应该包装在 action 中。

wrong!

mobx.configure({ enforceActions: true }) // 不允许在动作之外进行状态修改

class Store {
    @observable githubProjects = []
    @observable state = "pending" // "pending" / "done" / "error"

    @action
    fetchProjects() {
        this.githubProjects = []
        this.state = "pending"
        fetchGithubProjectsSomehow().then(
            projects => {
                const filteredProjects = somePreprocessing(projects)
                this.githubProjects = filteredProjects
                this.state = "done"
            },
            error => {
                this.state = "error"
            }
        )
    }
}

上面的例子会报错,因为fetchGithubProjectsSomehow().then(cb)中的cb不是fetchProjects动作的一部分,因为动作只会作用于当前栈,要修复它,简单思想是将promise的then变成当前动作的一部分

方案1:使用action.bound

right!

mobx.configure({ enforceActions: true })

class Store {
    @observable githubProjects = []
    @observable state = "pending" // "pending" / "done" / "error"

    @action
    fetchProjects() {
        this.githubProjects = []
        this.state = "pending"
        fetchGithubProjectsSomehow().then(this.fetchProjectsSuccess, this.fetchProjectsError)

    }

    @action.bound
    fetchProjectsSuccess(projects) {
        const filteredProjects = somePreprocessing(projects)
        this.githubProjects = filteredProjects
        this.state = "done"
    }
    @action.bound
        fetchProjectsError(error) {
            this.state = "error"
        }
    }

 

方案2:使用action关键字包装promise回调函数,需要给它们命名

right!

mobx.configure({ enforceActions: true })

class Store {
    @observable githubProjects = []
    @observable state = "pending" // "pending" / "done" / "error"

    @action
    fetchProjects() {
        this.githubProjects = []
        this.state = "pending"
        fetchGithubProjectsSomehow().then(
            // 内联创建的动作
            action("fetchSuccess", projects => {
                const filteredProjects = somePreprocessing(projects)
                this.githubProjects = filteredProjects
                this.state = "done"
            }),
            // 内联创建的动作
            action("fetchError", error => {
                this.state = "error"
            })
        )
    }
}

我们的开头的示例就是采用这种方式

 

如果我只想在动作中运行回调函数的状态修改部分,而不是为整个回调创建一个动作,这就形成了在整个过程结束时尽可能多的对所有状态进行修改

方案3:runInAction

mobx.configure({ enforceActions: true })

class Store {
    @observable githubProjects = []
    @observable state = "pending" // "pending" / "done" / "error"

    @action
    fetchProjects() {
        this.githubProjects = []
        this.state = "pending"
        fetchGithubProjectsSomehow().then(
            projects => {
                const filteredProjects = somePreprocessing(projects)
                // 将‘“最终的”修改放入一个异步动作中
                runInAction(() => {
                    this.githubProjects = filteredProjects
                    this.state = "done"
                })
            },
            error => {
                // 过程的另一个结局:...
                runInAction(() => {
                    this.state = "error"
                })
            }
        )
    }
}

还有async/await的处理,flows的高级用法,详见:https://cn.mobx.js.org/best/actions.html

 

 

 

转载于:https://my.oschina.net/u/2510955/blog/1833232

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值