React-Mobx 基础学习
Mobx 和 Redux 一样都是状态管理库,需要注意的是他们都是属于 js,即你可以在 React
中使用,也可以在 Angular
、Vue
中使用,本身并不会有限制。
正如大多数对比 Mobx 和 Redux 的文章–这里不对比孰好孰坏(实际上我觉得两个都挺好,甚至可以考虑混合使用),他们的特征都很明显。我觉得有如下差异(未完,待补充):
- Redux 是函数式(我们能可以比较清晰的知道他是如何工作的,但是用起来需要比较多的约定),而 Mobx 则更加面向对象(我们可能不是很容易知晓他如何工作,但是很容易就能够用起来)
- Redux 强调统一 State(一个 Store 管理全局,强规范没了灵活性,但是又把 Reducer、Type 变成了难维护的位置),而 Mobx 则更倾向于可以随意支配(是统治还是分治就看你需要,这个灵活性高就导致了可能出现的 State 维护问题)
由于 Observable 的特性,使得 Mobx 天生就对异步操作是兼容的,而 Redux 则需要做出一些修改才能做到
废话不多说,下面来看看 Mobx 中的基础概念(这里不会说的太详细,具体可以去官网查看文档,文中的部分概念也是来自于此)
Mobx 中的概念
State 状态
状态是一些被标识 @observable
的值(值可以是 JS 基本类型、引用类型、普通对象、类实例、映射),Mobx 中标记了 @observable
的值变化之后是会同步到应用中所有的这个状态所在的位置。
Mobx 中用 @observer
标记的 React 组件会被转变成响应式组件(由 mobx-react
包提供),以响应状态的变化(变化时强制刷新)
Derivations 衍生
任何来源于状态并且不会有任何进一步的相互作用的东西就被称为衍生,有如下形式:
- 用户界面
- 衍生数据,根据主数据的变化计算所得到的数据
- 后端集成,比如发送数据到后端
在 Mobx 中有一下两种大的衍生:
- Computed:你可以使用函数计算一些可观察状态的衍生值,使用时是对方法加上
@computed
属性,当视图不再使用它的时候,这个就会被自动回收垃圾 - Reactions:是一种当状态改变的时候的自动发生的副作用(一般用于 I/O 操作)
Mobx 中还有一些方式其他对 observable
响应
- autorun:自动运行的衍生计算,主要是用于打印日志、持久化甚至是刷新 dom,他与
computed
的使用场景官网也给了区分,当我们需要一个方法自动运行,但是不会产生新的值的时候,就使用autorun
,其余情况我们都使用computed
即可,第二个参数是选项,具体官网,但是这个不会自动回收垃圾 - when : 自定义的反应,观察第一个参数(
predicate
)返回true
的时候,就执行第二个参数(effect
)的方法。如果没有提供第二个参数(effect
)的话,when()
方法会返回一个promise
,第三个参数是选项,具体可以看官网 - reaction:接收三个参数,两个函数参数,只有当第一个函数返回新值的时候才会执行,且以第一个方法(
data
)的返回值作为第二个方法(effect
)的输入数据,第三个参数为选项,具体传送门
Actions 动作
与 Redux 中不一样的是,Mobx 中的 Action 相当于是 Redux 中的 Reducer,是用来描述我们要如何改变这个状态,比如用户的事件,后端的数据获取等等。
我们通过 @action.bound
来进行 Action 绑定(不需要 name
),但是要注意使用这种方式的话,不能使用箭头函数
@action.bound
public changeHover(){
//dosomething
}
另外可以通过 action()
方法来绑定,但是这个是需要 name
的 如:
action("createRandomContact-callback", (error, results) => {//dosomething})
严格模式
需要注意的是 Mobx 不强制要求必须要在 Actions 中才能改变状态
(但是推荐这样做),但是这样可能导致一些状态更新混乱而出现的奇怪问题,如果想要强制在 Actions 中更新状态的话,就需要启用严格模式
方式如下:
- 参见文档,这种方式可以开启全局严格模式
- 你也可以通过 Mobx 提供的
useStrict(true)
给状态打上严格模式,这样可以针对某个状态启用严格模式
另外如果你启用了严格模式,需要注意的是异步操作里面也需要一个action
进行包裹(因为 action
无法影响当前方法或者函数中的异步操作),这样才不会导致报错。
官网中的例子,例子中 end
后是一个异步操作,这时候需要使用 action
或者是 runInAction
包裹起来这个异步操作:
@action createRandomContact() {
this.pendingRequestCount++;
superagent
.get('https://randomuser.me/api/')
.set('Accept', 'application/json')
.end(action("createRandomContact-callback", (error, results) => {
if (error)
console.error(error);
else {
const data = JSON.parse(results.text).results[0];
const contact = new Contact(this, data.dob, data.name, data.login.username, data.picture);
contact.addTag('random-user');
this.contacts.push(contact);
this.pendingRequestCount--;
}
}));
}
原则
和 Redux 一样,Mobx 支持单项数据流,整体如下:
动作=>状态=>界面,如下图(图片来源官网,其他注意事项):
来个栗子
我们现在就又来动动手,实现一个 Logo Hover 之后切换图片,使得我们的 Logo 看起来更cool
整体页面的搭建我就不说,那个不是重点,我们效果现在如下:
想要达到 Hover 的时候就有如下效果:
目录结构如下:
建立 State、Action
修改 Header.state.ts
添加一个 可观察的 Observable
的值 isLogoHover
,以及一个 Action
用于修改前面的 isLogoHover
值
import { observable, action } from 'mobx';
class HeaderState {
// tslint:disable-next-line:variable-name
@observable public isLogoHover = false;
@action.bound
public changeHover(){
this.isLogoHover = !this.isLogoHover;
}
}
const HeaderComState = new HeaderState();
export default HeaderComState;
传入状态
修改 Header.tsx
我们在这里把 HeaderState
通过 React 的 props
传递到组件内部
import { headerState } from './../../../Mobx/index';
***类的其他代码
public render(){
const { headerState } = States;
return (
<div>
<HearderComponment store={headerState}/>
</div>
);
}
***类的其他代码
观察者
这里需要修改 HeaderComponent.tsx
添加 @observer
标记到 class
(Component
)上,把组件转换成响应式的,即在这里这个组件变成了观察者,并且通过前面所传递的 props
得到传递进入的 state
,在 img
的 hover
以及离开事件中调用 state
的 action
进行状态改变
import { observer } from 'mobx-react';
@observer
class HearderComponment extends React.Component<any, any>{
***类的其他代码
public render(){
let logo = null;
debugger;
const store = this.props.store;
if(store.isLogoHover){
// hover 后的图
logo = <img className="LogoText" src={LogoNexHover} height={60} onMouseOver={this.handleLogoMouseOver} onMouseLeave={this.handleLogoMouseOver}/>
}
else{
// hover 前的图
logo = <img className="LogoText" src={LogoNex} height={60} onMouseOver={this.handleLogoMouseOver} onMouseLeave={this.handleLogoMouseOver}/>
}
}
public handleLogoMouseOver(e:any){
this.props.store.changeHover();
}
}
***类的其他代码
运行,效果达到了
默认效果:
Hover 之后: