react-native 中使用 mobx
1. 介绍
1.1. 原理
React的render是 状态 转化为树状结构的渲染组件的方法
而MobX提供了一种存储,更新 状态 的方法
React 和 MobX都在优化着软件开发中相同的问题。
React 使用的方法是让虚拟DOM来减少繁琐而沉重的DOM变化。
而MobX则通过一个虚拟的状态依赖图表来让react组件和应用状态同步化来减少不必要的状态导致组件更新
1.2. 安装
MobX:
npm install mobx --save
React bindings:
npm install mobx-react --save
1.3. 要点
MobX看起来很复杂的样子,其实是用它只需要三步
- 定义你的状态,让它们成为观察者(observable)
存储状态(Store state)可以是任何的数据结构,随你定义为:对象,数组,类,循环结构,引用都没所谓。但需要记住一点,就是:随着时间的变化,用MobX 去把它们定义成观察者(observable)
import { observable } from 'mobx'
let appState = observable({
timer: 0
})
- 我们不需要让appState去观察什么。你现在就能创建视图(view),每当appState的相关数据发生变化的时候,就会自动更新。MobX会采用最优的方式去更新你的视图。以下有一个例子来说明如何使用,其中使用了ES6/ES7的语法(当然MobX也是支持ES5),代码中@的意义
import { observer } from 'mobx-react';
@observer
class TimerView extends React.Component {
render() {
return (
<button onClick={this.onReset.bind(this)}>
Seconds passed: {this.props.appState.timer}
</button>
);
}
onReset () {
//appState.resetTimer会在下一节完成
this.props.appState.resetTimer();
}
};
React.render(<TimerView appState={appState} />, document.body);
- 修改状态
第三节要说的是修改状态。MobX和其他框架不同,它不会要求你去做什么事情,它只是帮助你去做简单的事情
appState.resetTimer = action(function reset() {
appState.timer = 0;
}); setInterval(action(function tick() {
appState.timer += 1;
}), 1000);
其中action包装用法只能在strict模式下使用,请记得在你的javascript文件头写上:'use strict'。
2. API
从上面的例子可以看到,MobX的API其实不多:observable, computed, reactions, actions
2.1. observable(value)
其中的value可以是JS原定的数据结构,引用,对象,数组,ES6的map
- 如果value是一个map的话,则需要使用一个调节器(modifier)asMap来使用。这时候会返回一个Observable Map
- 如果是一个数组,返回Observable Array
- 如果是一个没有属性的对象,则返回一个Observable Object
- 如果是一个有属性的对象,JS原有的数据结构,函数等,返回一个** Boxed Observable**。MobX不会自动让一个有属性的对象成为观察者。这是这个有属性的对象的构造函数应该做的事情,你可以使用extendObservable在它的构造函数里面,或者在它的类使用@observable去定义。
以下是一些例子:
const map = observable(asMap({ key: "value"}));
map.set("key", "new value"); const list = observable([1, 2, 4]);
list[2] = 3; const person = observable({
firstName: "Clive Staples",
lastName: "Lewis"
});
person.firstName = "C.S."; const temperature = observable(20);
temperature.set(25);
2.2. @observable
import {observable} from "mobx";
class OrderLine {
@observable price:number = 0;
@observable amount:number = 1;
constructor(price) {
this.price = price;
}
//这里在下一节会说到
@computed get total() {
return this.price * this.amount;
}
}
const line = new OrderLine();
console.log("price" in line); // true
//hasOwnProperty:判断一个对象是否有你给出名称的属性或对象。需要注意,此方法无法检查该对象的原型链中是否具有该属性
console.log(line.hasOwnProperty("price")); //false
如果你的环境不支持ES6/7的语法的话,其实@observable key = value; 只是extendObservable(this, { key: value })的语法糖。因此在ES5环境下你也能使用
2.3. (@)computed
Computed values 就像一个算术公式一样去从现有的状态或其他值去计算出需要的值。计算的耗费是不可低估的。Computed尽可能帮你减少其中的耗费。它们是高度优化的,请把它用在可能用到的地方。
不要混淆下一节说到的autorun。虽然他们都是被动调用的表达式。但是……
Computed使用情况:如果你需要产生一个有观察者(observers)参数计算的新的值的时候
autorun使用情况:你不想产生一个新的值就想达到一个新的效果/功能。就像是打log或者进行网络请求
Computed values是自动帮你从你的状态(state)值和其他计算辅助值来计算的。MobX做了很多的优化。当参与计算的值没有发生改变,Computed是不会重新运行。如果参与计算的值没有被使用,Computed values是暂停的。
如果Computed values不再是观察者(observed),那么在UI上也会把它除掉,MobX能自动做垃圾回收。autorun则需要你自己手动去处理。如果参与计算的值不再被使用,是不会缓存Computed的,所以重新计算是需要的。这个是最理想的默认情况。如果你想保留,可以了解一下keepalive和observe。
例子1: 在2.2的例子。@computed get
例子2: @computed set
class Foo {
@observable length: 2,
@computed get squared() {
return this.length * this.length;
}
set squared(value) { //this is automatically an action, no annotation necessary
this.length = Math.sqrt(value);
}
}
需要注意的是:setter并非用于直接改变参数计算的值,如例子中的length。而是作为一个逆推导。
2.4. Autorun
Autorun是用在一些你想要产生一个不用观察者参与的被动调用函数里面。当autorun被使用的时候,一旦依赖项发生变化,autorun提供的函数就会被执行。与之相反的是,computed提供的函数只会在他有自己的观察员(observers)的时候才会评估是否重新执行,否则它的值被认为是无用的。
根据这些经验:如果你需要一个自动运行但却不会产生任何新的值的结果的函数,那么请使用Autorun。其他情况请使用computed。Autorun只是作用于如果达到某个效果或者功能,而不是计算某些值。如果有一个字符串作为第一个参数存入Autorun,那么它将成为一个调试名称。
var numbers = observable([1,2,3]);
var sum = computed(() => numbers.reduce((a, b) => a + b, 0)); var disposer = autorun(() => console.log(sum.get()));
// prints '6'
numbers.push(4);
// prints '10'
2.5. @observer
- observer 函数/修饰器用于react组件。通过mobx-react依赖包来提供。它通过mobx.autorun来包装了组件的render函数,以确保组件的render函数在任何数据的更改是强制重新渲染
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> )
}
});
React.render(<Timer timerData={timerData} />, document.body);
tips: 如果还有其他的decorators一起或者高阶组件的存在,请确保observer为最内层(优先应用)的修饰器。否则它可能无法工作。如果你只在ES5的环境下工作:其实observer不过是observer(class Timer ... { }) 的语法糖。
- 难点—组件中相关值的引用:
MobX能做的事情很多,但是它却不能把原始的值变成观察者(尽管可以通过包裹这个值来返回一个boxed observables的对象)。所以观察者不是这个原始的值,而是返回后的对象的属性值。修改一个刚才的例子:
React.render(<Timer timerData={timerData.secondsPassed} />, document.body)
这时候程序并不会工作了。传入组件的只是timerData里面secondsPassed的当前值。在组件里面,它是不可变的。
- 把你的组件内部状态变成可观察的
和普通的类一样,你可以在你的组件使用@observable修饰器。这意味着你的组件拥有了一个内部state,而且它不需要使用react内部提供的繁琐的setState机制。这个内部state能调起render函数,但是却不能准确调起React的生命周期函数,例如:componentShouldUpdate / componentWillUpdate。如果你想要这些,最好使用react提供的API来创建state。当然也可以这样写
import {observer} from "mobx-react"
import {observable} from "mobx"
@observer class Timer extends React.Component {
@observable secondsPassed = 0
componentWillMount() {
setInterval(() => {
this.secondsPassed++
}, 1000)
}
render() {
return (<span>Seconds passed: { this.secondsPassed } </span> )
}
})
React.render(<Timer />, document.body)
- 连接observer和stores
mobx-react提供了Provider组件让你可以把传递下来的stores作用在react提供的上下文机制。通过连接这些stores和observer,这些observer会成为组件的属性来使用。
const colors = observable({
foreground: '#000',
background: '#fff'
}); const App = () =>
<Provider colors={colors}>
<app stuff... />
</Provider>; const Button = observer(["colors"], ({ colors, label, onClick }) =>
<button style={{
color: colors.foreground,
backgroundColor: colors.background
}}
onClick={onClick}
>{label}<button>
);
// later..
colors.foreground = 'blue';
// all buttons updated
- componentWillReact
React 的组件总是从新的堆栈去渲染。因此让它它很难判断一个组件是否需要重新渲染。在mobx-react里面,你可以使用重新定义的生命周期componentWillReact。它只会在观察者发生变化的时候才重新渲染。
import {observer} from "mobx-react";
@observer class TodoView extends React.Component {
componentWillReact() {
console.log("I will re-render, since the todo has changed!");
}
render() {
return <div>this.props.todo.title</div>;
}
}
componentWillReact没有任何参数,而且不会在render初始化之前执行(componentWillMount的区别)。而当接收新的属性或者setState之后,它会被调用。
2.6. action
- 任何应用程序都有操作(action)。action是任何改变状态的事物。使用MobX,您可以通过标记它们在您的代码中显式地显示您的操作(action)。它会更好的帮助你组织你的代码。建议将它们用于修改可观察量或具有副作用的任何函数中。
需要注意的是:action是用在strict mode 中的
action(fn)
action(name, fn)
@action classMethod() {}
@action(name) classMethod () {}
@action boundClassMethod = (args) => { body }
@action(name) boundClassMethod = (args) => { body }
@action.bound classMethod() {}
@action.bound(function() {}) @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--;
}
}
))}
- action 仅仅作用于当前运行的函数,而不能作用于当前函数调用的函数。这意味着在一些定时器或者网络请求,异步处理的情况下,它们的回调函数无法对状态做成改变。这些回调函数都应该有action包裹,如果例子里面的 createRandomContact-callback 一样。但是,如果你使用了async / await的话,最好的方式应该是使用 runInAction 来让它变得更加简单
@action /*optional*/ updateDocument = async () => {
const data = await fetchDataFromUrl();
/* required in strict mode to be allowed to update state: */
runInAction("update state after fetching data", () => {
this.data.replace(data);
this.isSaving = true;
})
}
- Bound actions
目前看到的actions都是遵循在javascript中绑定的正常规则,但是在MobX 3引入了action.bound来自动绑定actions到目标对象上。和action的使用不一样,不需要一个名字参数。它的名称始终基于绑定到属性的操作上。需要注意的是,在箭头函数上不要这样使用,因为箭头函数已经绑定了上下文,不能在重新更改上下文
class Ticker {
@observable this.tick = 0 @action.bound
increment() {
this.tick++ // 'this' will always be correct
}
}
const ticker = new Ticker()
setInterval(ticker.increment, 1000)
.
react-native 中使用 mobx的更多相关文章
- React Native 中 CSS 的使用
首先声明,此文原作者为黎 跃春 React Native中CSS 内联样式 对象样式 使用Stylesheet.Create 样式拼接 导出样式对象 下面的代码是index.ios.js中的代码: / ...
- react native中的欢迎页(解决首加载白屏)
参照网页: http://blog.csdn.net/fengyuzhengfan/article/details/52712829 首先是在原生中写一些方法,然后通过react native中js去 ...
- React Native中的网络请求fetch和简单封装
React Native中的网络请求fetch使用方法最为简单,但却可以实现大多数的网络请求,需要了解更多的可以访问: https://segmentfault.com/a/1190000003810 ...
- [转] 「指尖上的魔法」 - 谈谈 React Native 中的手势
http://gold.xitu.io/entry/55fa202960b28497519db23f React-Native是一款由Facebook开发并开源的框架,主要卖点是使用JavaScrip ...
- [转] 在React Native中使用ART
http://bbs.reactnative.cn/topic/306/%E5%9C%A8react-native%E4%B8%AD%E4%BD%BF%E7%94%A8art 前半个月捣腾了一下Rea ...
- react native中使用echarts
开发平台:mac pro node版本:v8.11.2 npm版本:6.4.1 react-native版本:0.57.8 native-echarts版本:^0.5.0 目标平台:android端收 ...
- react native中一次错误排查 Error:Error: Duplicate resources
最近一直在使用react native中,遇到了很多的坑,同时也学习到了一些移动端的开发经验. 今天在做一个打包的测试时,遇到了一个问题,打包过程中报错“Error:Error: Duplicate ...
- 在React Native中,使用fetch网络请求 实现get 和 post
//在React Native中,使用fetch实现网络请求 /* fetch 是一个封装程度更高的网络API, 使用了Promise * Promise 是异步编程的一种解决方案 * Promise ...
- 《React Native 精解与实战》书籍连载「React Native 中的生命周期」
此文是我的出版书籍<React Native 精解与实战>连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理.React Native 组件布局.组件与 ...
- react native中如何往服务器上传网络图片
let common_url = 'http://192.168.1.1:8080/'; //服务器地址 let token = ''; //用户登陆后返回的token /** * 使用fetch实现 ...
随机推荐
- 制定ip池内随机生成ip地址
]) { +] = {}; unsigned mask = 0x0; sscanf(ip_pool, "%[^/]/%d", ip_addr, &mask); long l ...
- 原 android重启应用(应用重新启动自身)
private void restartApplication() { final Intent intent = getPackageManager().getLaunchIntentForPack ...
- 一个boost底下的线程池
Boost的thread库中目前并没有提供线程池,我在sorceforge上找了一个用boost编写的线程池.该线程池和boost结合的比较好,并且提供了多种任务执行策略,使用也非常简单. 下载地址: ...
- mysq 中 information_schema 库
information_schema这个库,这个在mysql安装时就有了,提供了访问数据库元数据的方式.那什么是元数据库呢?元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等.有些时 ...
- 使用bottle进行web开发(2):http request
我们知道,http request有多个方法,比如get,post,delete,patch,put等.对用的,bottle都定义了相应的装饰器,目前定义了五个: get(),post(),put() ...
- J.U.C并发框架源码阅读(六)ConditionObject
基于版本jdk1.7.0_80 java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject 代码如下 /** * Con ...
- 计蒜客 28319.Interesting Integers-类似斐波那契数列-递推思维题 (Benelux Algorithm Programming Contest 2014 Final ACM-ICPC Asia Training League 暑假第一阶段第二场 I)
I. Interesting Integers 传送门 应该是叫思维题吧,反正敲一下脑壳才知道自己哪里写错了.要敢于暴力. 这个题的题意就是给你一个数,让你逆推出递推的最开始的两个数(假设一开始的两个 ...
- 51nod 1420 数袋鼠好有趣【贪心】
1420 数袋鼠好有趣 题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 收藏 关注 有n只袋鼠.每只袋鼠的大小用一个整数表示. ...
- Python与数据结构[1] -> 栈/Stack[1] -> 中缀表达式与后缀表达式的转换和计算
中缀表达式与后缀表达式的转换和计算 目录 中缀表达式转换为后缀表达式 后缀表达式的计算 1 中缀表达式转换为后缀表达式 中缀表达式转换为后缀表达式的实现方式为: 依次获取中缀表达式的元素, 若元素为操 ...
- Django QuerySet 进阶
QuerySet 进阶 阅读本文你可以学习到什么 1. 查看 Django queryset 执行的 SQL(1部分) 2. 获得的查询结果直接以类似list方式展示(2,3 部分) 3. 如何在dj ...