这一节主要看mobx怎么实现asynchronous actionsjavascript
输入地名,查询天气,利用openweathermap apihtml
observable观察数据:location地点、temperature温度java
observer响应式组件:git
actions: location输入更新、点击add异步查询天气github
App:Container组件json
// 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组件api
@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组件promise
@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组件app
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) } }
action:动做是任何用来修改状态的东西;只会对当前运行的函数做出反应,而不会对当前运行函数所调用的函数(不包含在当前函数以内)做出反应,这意味着若是 action 中存在 setTimeout
、promise 的 then
或 async
语句,而且在回调函数中某些状态改变了,那么这些回调函数也应该包装在 action
中。dom
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