一.运用Mobx的步骤
mobx看上去api非常丰富,但使用 MobX 将一个应用(不仅仅是react应用)变成响应式的可归纳为以下三个步骤:
1.定义状态并使其可以观察
使用observable定义一个观察的对象,该对象可以是数据类型、引用类型、普通对象、类实例、数组和映射。例如观察一个对象,它包含属性值和方法:
let appState = observable({
timer: 10,
get getTimer() {
return this.timer
},
setTimer(timer: number) {
this.timer = timer
},
resetTimer() {
this.timer = 0
}
}
)
mobx还提供了装饰器的方式@observable,它适用于ES7或TS类中的属性使用。例如:
@observable name = 0;
@observable obj = {
name1: 'bobo',
name2: 'lili'
}
基于以上两种方式,一个被观察的内容就定义出来了。
2.创建视图以响应状态的变化
(1)使用@computed响应
@computed能自动的对现有状态做出响应(类似与vue的computed),例如下面代码能自动对appState.timer的变化做出响应:
@computed get getTimer () {
return this.props.appState.timer * -1
}
(2)使用(@)observer响应
(a)在react class component中
@computed仅仅可以作用在类属性的方法上,如果我想将一个组件转换为响应式组件,那么 (@)observer能在依赖的数据改变后强制刷新组件(注意:observer由mobx-react独立提供)。@observer和observer(class Timer ... { })的效果是一致的,但是前者更简明。下面这个例子首先定义了一个可观察对象timerData,
然后将Timer组件作为观察者对timerData做出响应。
import {observer} from "mobx-react";
var timerData = observable({
secondsPassed: 0
});
setInterval(() => {
timerData.secondsPassed++;
}, 1000);
@observer class Timer extends React.Component {
render() {
return (<span>Seconds passed: { this.props.timerData.secondsPassed } </span> )
}
};
ReactDOM.render(<Timer timerData={timerData} />, document.body);
(b)在无状态组件中
在无状态组件中,@observer不再适用,但observer仍然适用:
import {observer} from "mobx-react";
const Timer = observer(({ timerData }) =>
<span>Seconds passed: { timerData.secondsPassed } </span>
);
3.更改状态
使用(@)action能够显示的把更改操作标记出来,当然不使用(@)action也可以对状态做出更改。使用(@)action具有更高的性能,因为在更改动作完成之前,状态的改变在外界是不可见的。
二.mobx+hook如何工作
结合mobx与hook,你需要使用上mobx-react-lite。
1.跑通mobx+hook
先看一个错误的例子:
import {observer} from 'mobx-react-lite'
import {observable} from "mobx";
import React from 'react'
export default observer(() => {
const store = observable({
timer: 0
});
function add() {
store.timer += 1
console.log('当前的timer---->', store.timer)
}
return (
<div className="App">
<h2 onClick={() => add()}>Now time is {store.timer}</h2>
</div>
)
})
执行上面的代码,当点击h2标签后,发现页面内容不发生改变,控制台输出相同的内容:
在mobx-react-lite里面提供了一个useObserable方法,使用它能对视图进行更新。按下列的写法就能得到一个正确的结果:
import {observer, useObservable} from 'mobx-react-lite'
import React from 'react'
export default observer(() => {
const store = useObservable({
timer: 0
});
function add() {
store.timer += 1
console.log('当前的timer---->', store.timer)
}
return (
<div className="App">
<h2 onClick={() => add()}>Now time is {store.timer}</h2>
</div>
)
})
但是,使用useObserable不是唯一的解决方式,mobx-react-lite里面提供了一个Obserable组件也能更新所包裹的内容:
import {observer, Observer} from 'mobx-react-lite'
import {observable} from 'mobx'
import React from 'react'
export default observer(() => {
const store = observable({
timer: 0
});
function add() {
store.timer += 1
console.log('当前的timer---->', store.timer)
}
return (
<div className="App">
<Observer>
{() => <h2 onClick={() => add()}>Now time is{store.timer}</h2>}
</Observer>
</div>
)
})
也许你注意到了useObserver了,它也同样能给你提供一个正确的结果:
import {observer, useObserver,useObservable} from 'mobx-react-lite'
import {observable} from 'mobx'
import React from 'react'
export default (() => {
const store = useObservable({
timer: 0
});
function add() {
store.timer += 1
console.log('当前的timer---->', store.timer)
}
return (
useObserver(() => (
<div className="App">
<h2 onClick={() => add()}>Now timer is{store.timer}</h2>
</div>
))
)
})
接下来再看一个错误的例子,大家看看得到的结果是什么,为什么会这样?
import {useObservable} from 'mobx-react-lite'
import React from 'react'
export default (() => {
const store = useObservable({
timer: 0
});
function add() {
store.timer += 1
console.log('当前的timer---->', store.timer)
}
return (
<div className="App">
<h2 onClick={() => add()}>Now time is {store.timer}</h2>
</div>
)
})
当点击页面内容后发现控制台能正确输入:
useobervable能正确的得到当前的结果,但是没有一个渲染机制能将结果重新渲染到页面之上。这个错误的例子和本节第一个错误的例子有类似之处,第一个是没有得到正确的当前值,这个例子是没有将正确结果渲染出来。
为了证明我的说法,使用useEffect能证明页面有没有重新更新,因为useEffect能在每次更新的时候执行,下列代码中useEffect的函数并没有每次点击后执行:
import {useObservable} from 'mobx-react-lite'
import React, {useEffect} from 'react'
export default (() => {
const store = useObservable({
timer: 0
});
useEffect(() => {
console.log('页面进行了重新渲染') //点击后没有执行
})
function add() {
store.timer += 1
console.log('当前的timer---->', store.timer)
}
return (
<div className="App">
<h2 onClick={() => add()}>Now time is {store.timer}</h2>
</div>
)
})
那么怎么修改上方代码才能让页面渲染呢,你可能已经想出了一系列方法。但是万变不离其宗,你需要一个创建响应视图,如上方的正确代码,下面是另一种写法,使用observer响应timer的变化:
import {useObservable, observer} from 'mobx-react-lite'
import React from 'react'
export default (() => {
const store = useObservable({
timer: 0
});
function add() {
store.timer += 1
console.log('当前的timer 是---->', store.timer)
}
const ObserverH2 = observer(() =>
<h2 onClick={() => {add()}}>Now timer is {store.timer}</h2>
)
return (<ObserverH2/>)
})
2.useComputed
与computed类似,作为计算属性,它能自动监听依赖的变化:
let computedTimer = useComputed(() => {
return store.timer
}, [store])
3.useLocalStore
使用useLocalStore可以将一些内容封装在一个根对象中,借助React Context将其传递给应用程序。
import {useObserver,useLocalStore} from 'mobx-react-lite'
import React from 'react'
export default (() => {
const store = useLocalStore(() => ({
timer: 0
}));
function add() {
store.timer += 1
console.log('当前的timer---->', store.timer)
}
return (
useObserver(() => (
<div className="App">
<h2 onClick={() => add()}>Now timer is{store.timer}</h2>
</div>
))
)
})
上面的useLocalStore同useObservable用法完全一致,但是这不是useLocalStore的合理用法,借助react的contex,可以将store的内容共享。这里有一份官方的迁移文档,里面有更详细的用法。
总结
本文先是介绍Mobx的使用方法,然后又在react hook中尝试使用,由于初次使用Mobx,错误之处,敬请原谅。
上一篇:react hook 初体验