在之前的文章中使用了observer包装App, 实现了页面的动态刷新,这里主要了解一下另外两种reaction的实现:when和autorun
When
when(predicate: () => boolean, effect?: () => void, options?)
when
观察并运行给定的 predicate
,直到返回true。 一旦返回 true,给定的 effect
就会被执行,然后 autorunner(自动运行程序) 会被清理。 该函数返回一个清理器以提前取消自动运行程序。
也就是说,when的使用可以传入有前置条件
下面的例子使用when实现在温度>25的时候,会alert出这个城市的名字
import ReactDOM from 'react-dom'
import React from 'react'
import { action, computed, observable, when } from 'mobx'
import { Provider, observer, inject } from 'mobx-react'
import DevTools from 'mobx-react-devtools'
const APPID = '415a88f2b45f08c3e561b058772ec6c3'
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)
}
}
@inject('temperatures')
@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 = ''
}
}
@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()
}
}
const App = inject('temperatures')(observer(
({ temperatures }) => (
<ul>
<TemperatureInput />
{temperatures.map(t =>
<TView key={t.id} temperature={t} />
)}
<DevTools/>
</ul>
)))
const temps = observable([])
ReactDOM.render(
<Provider temperatures={temps}>
<App />
</Provider>,
document.getElementById('root')
)
/* 判断温度是否适宜 */
function isNice (t) {
return t.temperatureCelsius > 25
}
// 如果temps中有温度>25的, 就执行第二个函数,找到第一个适宜的温度(> 25), alert
when(
() => temps.some(isNice),
() => {
const t = temps.find(isNice)
alert('Book now! ', t.location)
}
)
autorun
当你想创建一个响应式函数,而该函数本身永远不会有观察者时,可以使用 mobx.autorun
当使用 autorun
时,所提供的函数总是立即被触发一次,然后每次它的依赖关系改变时会再次被触发。
经验法则:如果你有一个函数应该自动运行,但不会产生一个新的值,请使用autorun
。 其余情况都应该使用 computed
var numbers = observable([1,2,3]);
var sum = computed(() => numbers.reduce((a, b) => a + b, 0));
var disposer = autorun(() => console.log(sum.get()));
// 输出 '6'
numbers.push(4);
// 输出 '10'
disposer();
numbers.push(5);
// 不会再输出任何值。`sum` 不会再重新计算。
我们的某个地方获得天气的例子,使用autorun来更新UI
import { action, computed, observable, when, autorun } from 'mobx'
const APPID = '415a88f2b45f08c3e561b058772ec6c3'
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)
}
}
const temps = observable([])
/* 判断温度是否适宜 */
function isNice (t) {
return t.temperatureCelsius > 25
}
function render (temperatures) {
return `
<ul>
${temperatures.map(t =>
`<li>
${t.location}:
${t.loading ? 'loading' : t.temperature}
</li>
`
).join('')}
</ul>
`
}
temps.push(new Temperature('Amsterdam'))
temps.push(new Temperature('Rotterdam'))
when(
() => temps.some(isNice),
() => {
const t = temps.find(isNice)
alert('Book now! ', t.location)
}
)
autorun(() => {
document.getElementById('app').innerHTML = render(temps)
})