初识 MobX + React-Native + React R
MobX
简单、可扩展的状态管理(可观察的数据)
使用:
安装: npm install mobx --save
。
React 绑定库: npm install mobx-react --save
。
引用 : import { observable } from 'mobx';
Api
observable
什么是 observable ?
observable 是一种让数据的变化可观察的方法
哪些数据类型可以被观察 ?
- 原始类型:string,number,boolean,symbol
- 对象
- 数组
如何用 observable 把对象转化为可观察到对象
MobX 对任意变量的处理方式有两种
1. 对于数组纯对象以及 es6 中的 map ,直接把 observable 当作函数来把变量转化为可观察到对象,之后对数组、对象、map 中的内部数据进行合理的操作,都将会被监视
const map = observable(new Map());
map.set('a',1);
console.log(map.has('a'));
map.delete('a');
console.log(map.has('a'));
2. 对于上面没有包含的其他类型,都将调用 observable.box
来进行把变量包装为可观察到对象,之后对该变量的直接负值将会被监视
const str = observable.box(2);
const num = observable.box(2);
const bool = observable.box(2);
num.set(3)
console.log('---observable---', str, num.get(), bool.get())
-
.get()
- 返回原始类型值。 -
.set(value)
- 替换当前存储的值并通知 observable。
对于 todolist 的 demo 来讲
- 每一次对Todos更改都会触发change(例如:添加,删除)
- 但是对Todos中的每一条Todo状态更改并不会被监听(不会监听下一级变化)
- change的object属性代表改变之后的状态
constructor() {
observe(this.Todos, change => {
console.log(change,'this.Todos, change')
})
}
MobX 提供了 decorator 的修饰器
其只可以修饰类和类成员
MobX 为了简化 API 让 @observable 能识别当前是被普通函数调用的还是被当作修饰器调用的,如果是修饰器(@)就自动识别变量类型
spy
用法:
spy(listener) 注册一个全局间谍监听器,用来监听所有 MobX 中的事件。 它类似于同时在所有的 observable 上附加了一个 observe 监听器,而且还通知关于运行中的事务/反应和计算。
spy(event => {
console.log(event,'spy-event')
});
computed
什么是 computed ?
-
一般理解为对可观察数据做出的响应
-
多个可观察数据组合成一个可观察数据
import {observable, computed, autorun} from "mobx";
class Store {
@observable
price = 6;
@observable
amount = 3;
}
const store = new Store()
var total = computed(()=>{
return store.price * store.amount;
})
total.observe((change)=>{
console.log(total,'computed---------',change)
})
store.price = 9;
简而言之,你有一个值,该值的结果依赖于其他被观察的值,并且该值也需要被 obserable,那么就使用computed。
autorun
什么是 autorun ?
另一个响应 state 的 api 便是 autorun 。和 computed 类似,每当依赖的值改变时,其都会改变。不同的是, autorun 没有了 computed 的优化(当然,依赖值未改变的情况下也不会重新运行,但不会被自动回收)。因此在使用场景来说, autorun 通常用来执行一些有副作用的。例如打印日志,更新 UI 等等。
官方定义:
当你想创建一个响应式函数,而该函数本身永远不会有观察者时,可以使用 mobx.autorun。 这通常是当你需要从反应式代码桥接到命令式代码的情况,例如打印日志、持久化或者更新UI的代码。 当使用
autorun 时,所提供的函数总是立即被触发一次,然后每次它的依赖关系改变时会再次被触发
。
相比之下,computed(function) 创建的函数只有当它有自己的观察者时才会重新计算,否则它的值会被认为是不相关的
。 经验法则:如果你有一个函数应该自动运行,但不会产生一个新的值,请使用autorun。 其余情况都应该使用 computed。 Autoruns 是关于 启动效果 (initiating effects) 的 ,而不是产生新的值。 如果字符串作为第一个参数传递给 autorun ,它将被用作调试名。
简而言之:自动追踪其所引用的可观察数据,并在数据发生变化时重新触发
假如你观察了一个数组,你想根据数组的长度变化作出反应,在不使用 computed 时,代码是这样的
var numbers = observable([1, 2, 3]);
autorun(() => console.log(numbers.length));
// 输出 '3'
numbers.push(4);
// 输出 '4'
numbers[0] = 0;
// 输出 '4'
使用 computed 最后一个改变数组内元素内容的改变就不会被触发
const Mobx = require("mobx");
const { observable, autorun, computed } = Mobx;
var numbers = observable([1, 2, 3]);
var sum = computed(() => numbers.length);
autorun(() => console.log(sum.get()));
// 输出 '3'
numbers.push(4);
// 输出 '4'
numbers[0] = 1;
总结:
- computed 可以当作新的可观察数据看待
- autorun 引用的可观察数据发生的变化 无论是否修改数据都会先行执行一次
如果你想响应式的产生一个可以被其它 observer 使用的值,请使用 @computed,如果你不想产生一个新值,而想要达到一个效果(比如上文提到的 打印日志、持久化或者更新UI的代码,请使用 autorun。
when
提供了条件执行逻辑
class Store {
@observable
bool = false;
}
const store = new Store()
when(()=>store.bool, ()=>console.log('when~'));
console.log('-----')
store.bool = true
// -----
// when~
如果一开始 bool 就是 true 那 when 就会立即执行
action
什么是 action ?
action 是任何用来修改状态的东西
在 MobX 中,对于 store 对象中可观察的属性值,在他们改变的时候则会触发观察监听的函数,这里注意两点:
- 该属性必须是定义的可观察属性(@observable)
- 它的值必须发生改变(和原值是不等的)
何时使用action
应该永远只对修改状态的函数使用 action。
只执行查找,过滤器等函数不应该被标记为动作,以允许 MobX 跟踪它们的调用。
对于 todolist 中的 demo 来讲
@action
createTodo(title:string,content:string) {
this.Todos.unshift(new Todo(title,content));
}
@action
createTodo(Todo:IINSTodo) {
this.Todos.remove(Todo)
}
createTodo 和 createTodo 会将 被观察的 Todos 改变
Intercept & Observe
MobX 还提供了很多好用的方法,例如上面代码中的 this.Todos.remove(Todo)
的remove方法可以直接从数组中删除某元素,不用遍历查找再删除
定义数据存储
可以通过在应用中传递属性到组件树中或使用 mobx-react 包中的 Provider 和 inject 来分发 UI 状态 store。
Provider
和 Inject
Provider 是一个组件,可以使用 react 上下文机制将 store (其他东西)传递给子组件。如果你不希望显示传递多层组件请使用 provider
inject 可以获得 store 它是一个更高阶的组件,它接受一个字符串列表并使这些存储可用于被包装的组件。
MobX 学习参考:
https://github.com/ckinmind/mobx-share
React Native 基础了解
常用标签(组件)篇
使用: import { View, Text, TextInput, TouchableOpacity } from "react-native";
View
相当于 html 的 div,块容器
Image
图片展示组件,常用属性如下:
source
source={{uri: "XXX"}}
加载网络图片, 必须设置宽和高才能展示。在展示图片前,最好判断XXX是否存在
source={require("XXX")}
加载本地图片,XXX为本地图片相对地址
TouchableOpacity
用于写按钮的组件。常用属性如下:(此组件与TouchableHighlight的区别在于并没有额外的颜色变化,更适于一般场景)
-
activeOpacity
指定封装的视图在被触摸操作激活时以多少不透明度显示(通常在 0 到 1 之间) -
onPress
类似平时写的 onClick
TouchableHighlight
用于写按钮的组件,常用属性如下:
-
activeOpacity
指定封装的视图在被触摸操作激活时以多少不透明度显示(通常在 0 到 1 之间) -
underlayColor
有触摸操作时显示出来的底层的颜色 -
onPress
类似平时写的 onClick
Text
显示文字的组件 ( 文字不能直接写在 View 中要嵌套 Text )
<View><Text> 要显示的文字 </Text></View>
-
ellipsizeMode
取值('head', 'middle', 'tail', 'clip')-
head:
从文本的开头进行截断,并在文本的开头添加省略号,例如:…xyz。 -
middle :
从文本的中间进行截断,并在文本的中间添加省略号,例如:ab…yz。 -
tail:
从文本的末尾进行截断,并在文本的末尾添加省略号,例如:abcd…。 -
clip :
文本的末尾显示不下的内容会被截断,并且不添加省略号,clip只适用于iOS平台。
-
head:
-
numberOfLines
限制最多显示的行数 -
onPress
类似平时写的 onClick
TextInput
输入框组件
-
value
-
onChangeText
-
underlineColorAndroid="transparent"
TextInput在安卓上默认有一个底边框,同时会有一些padding。如果要想使其看起来和iOS上尽量一致,则需要设置padding: 0,同时设置underlineColorAndroid="transparent"来去掉底边框 -
placeholder
提示文本 -
placeholderTextColor
提示文本颜色 -
onBlur
当文本框失去焦点的时候调用此回调函数。 -
onFocus
当文本框获得焦点的时候调用此回调函数。
ScrollView
-
horizontal
当此属性为true的时候,所有的子视图会在水平方向上排成一行,而不是默认的在垂直方向上排成一列。默认值为false。 -
showsHorizontalScrollIndicator
当此属性为true的时候,显示一个水平方向的滚动条。
StyleSheet
样式定义 StyleSheet.create
style的写法基本就与正常的css相同,只不过用中划线的css名字改用驼峰式写法
例如: margin-top
改为 marginTop
Dimensions
获取屏幕尺寸 :
Dimensions.get('window').height
Dimensions.get('window').width
布局
路由
在 App.ts 中定义路由
import { NativeRouter, Route } from "react-router-native";
...
<NativeRouter>
<Route exact={true} path="/" component={index} />
<Route path="/Content" component={Content} />
</NativeRouter>
其中 exact
是 Route 下的一条属性,一般而言,react 路由会匹配所有匹配到的路由组价,exact 能够使得路由的匹配更严格一些。
-
exact 的值为 bool 型,为 true 是表示严格匹配,为 false 时为正常匹配。
-
如在 exact 为 true 时,' /link ' 与 ' / ' 是不匹配的,但是在 false 的情况下它们又是匹配的。
注意: react-router 还分为浏览器端(h5)和 rn应用端
- 如果你写h5,你应该安装 react-router-dom;
API
<BrowserRouter>
basename: string
getUserConfirmation: func
forceRefresh: bool
keyLength: number
children: node
<HashRouter>
basename: string
getUserConfirmation: func
hashType: string
children: node
<Link>
to: string
to: object
replace: bool
innerRef: function
innerRef: RefObject
others
<NavLink>
activeClassName: string
activeStyle: object
exact: bool
strict: bool
isActive: func
location: object
aria-current: string
<Prompt>
<MemoryRouter>
initialEntries: array
initialIndex: number
getUserConfirmation: func
keyLength: number
children: node
<Redirect>
to: string
to: object
push: bool
from: string
exact: bool
strict: bool
<Route>
Route render methods
Route props
component
render: func
children: func
path: string | string[]
exact: bool
strict: bool
location: object
sensitive: bool
<Router>
history: object
children: node
<StaticRouter>
basename: string
location: string
location: object
context: object
children: node
<Switch>
location: object
children: node
history
history is mutable
location
match
null matches
matchPath
pathname
props
withRouter
Component.WrappedComponent
wrappedComponentRef: func
例子:
import { HashRouter as Router, Route } from "react-router-dom";
...
<Router>
<div style={{ display: "flex", height: "100%" }}>
<Route exact={true} path="/" component={xxx} />
...
</div>
</Router>
- 如果你写 React Native 应用,你应该安装 react-router-native;
API:
<BackButton>
children
<DeepLinking>
<Link>
to: string
to: object
replace: bool
component: func
<NativeRouter>
getUserConfirmation: func
keyLength: number
children: node
<MemoryRouter>
initialEntries: array
initialIndex: number
getUserConfirmation: func
keyLength: number
children: node
<Redirect>
to: string
to: object
push: bool
from: string
exact: bool
strict: bool
<Route>
Route render methods
Route props
component
render: func
children: func
path: string | string[]
exact: bool
strict: bool
location: object
sensitive: bool
<Router>
history: object
children: node
<StaticRouter>
basename: string
location: string
location: object
context: object
children: node
<Switch>
location: object
children: node
history
history is mutable
location
match
null matches
matchPath
pathname
props
withRouter
Component.WrappedComponent
wrappedComponentRef: func
例子:
import { NativeRouter, Route } from "react-router-native";
...
<NativeRouter>
<Route exact={true} path="/" component={xxx} />
<Route path="/xxx" component={xxx} />
</NativeRouter>
参考:
react-router 文档
REACT ROUTER <- 这个讲的很详细,上述的API在其中有代码演示
更多参考: react native 中文网