MobX 给你久违已久的编程风格
react-native 指令编译运行
总是忘记几个命令这里就专门记录下来。<我在Mac air 终端执行>
查看当前可用的设备/模拟器的列表:xcrun simctl list devices
指定模拟器平台编译运行:
react-native run-ios --simulator "iPhone Xʀ"
MobX 在react-native使用
Mobx使用前错误纠正
No bundle URL present.
No bundle URL present. Make sure you’re running apackager server …
我的成功解决方案: rm -r ./ios/build && react-native run-ios --simulator "iPhone Xs"
SyntaxError: Support for the experimental syntax ‘decorators-legacy’ isn’t currently enabled
SyntaxError: Support for the experimental syntax ‘decorators-legacy’ isn’t currently enabled
~~我的成功解决方案``:安装"@babel/plugin-proposal-decorators": "^7.4.4"
并配置,新建文件.babelrc
参考overflow信息
//.babelrc
{
"plugins": [["@babel/plugin-proposal-decorators", { "legacy": true }]]
}
null is not an object (evaluating ‘_RNGestureHandlreModule.default.Direction’)
出现这个错误的原因是,我在项目中配个导航组件React Navigation
出错环境 Mac Air
react | react-native | react-navigation | react-native-gesture-handler | react-native-reanimated |
---|---|---|---|---|
“16.8.3” | “0.60.4” | “^3.11.1” | “^1.3.0” | “^1.1.0” |
找了好久,没得解决方法
最后没办法我就换了react-native的Version号,好像说是0.60.4版本在这方面是个bug,还没解决!!
react | react-native | react-navigation | react-native-gesture-handler | react-native-reanimated | mobx | mobx-react | @babel/plugin-proposal-decorators |
---|---|---|---|---|---|---|---|
“16.8.3” | “0.59.9” | “^3.11.1” | “^1.3.0” | “^1.1.0” | “^5.13.0” | “^6.1.1” | “^7.4.4” |
配置完了,页面的导航,于是写个简单demo。先看首页和源码:附源码
import React, { Component } from "react";
import { View, Text, Platform, StatusBar } from "react-native";
import { observer, inject } from "mobx-react";
import { observable, action, autorun } from "mobx";
import { Label, Button } from "teaset";
const instructions = Platform.select({
ios: "Press Cmd+R to reload,\n" + "Cmd+D or shake for dev menu",
android:
"Double tap R on your keyboard to reload,\n" +
"Shake or press menu button for dev menu"
});
const dsposer = autorun(Num => console.log("打印监听的Num数:", Num));
@observer
export default class HomePage extends Component {
static navigationOptions = {
title: "首页",
headerBackTitle: " ", //下一级页面就不会显示 "首页" 二字了
headerStyle: { backgroundColor: "#f4511e" },
headerTintColor: "#FFF",
headerTitleStyle: { fontWeight: "bold", fontSize: 20 }
};
@observable
Num = 0;
componentWillMount() {
StatusBar.setBarStyle("light-content");
console.log("打印监听 componentWillMount ");
}
componentDidMount() {
console.log("打印监听 componentDidMount ");
}
componentWillUpdate() {
console.log("打印监听 componentWillUpdate ");
}
componentDidUpdate() {
console.log("打印监听 componentDidUpdate ");
}
componentWillReceiveProps() {
console.log("打印监听 componentWillReceiveProps ");
}
// shouldComponentUpdate() {
// console.log("打印监听 shouldComponentUpdate ");
// }
render() {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>{instructions}</Text>
<Button
type="primary"
size="lg"
title="跳转到二维码图片页面"
onPress={this.gotoGeneratorQr}
titleStyle={{ color: "#333" }}
style={{ marginTop: 10, backgroundColor: "#fcf8e3" }}
/>
<View
style={{
flexDirection: "row",
height: 45,
justifyContent: "center",
marginTop: 15
}}
>
<Button
type="danger"
size="sm"
title="数量减"
onPress={this.decreaseNum}
titleStyle={{ color: "#333" }}
style={{
marginHorizontal: 10,
backgroundColor: "#fcf8e3",
width: 80
}}
/>
<Label type="title" size="xl" text={this.Num} />
<Button
type="danger"
size="sm"
title="数量增"
onPress={this.increaseNum}
titleStyle={{ color: "#333" }}
style={{
marginHorizontal: 10,
backgroundColor: "#fcf8e3",
width: 80
}}
/>
</View>
</View>
);
}
gotoGeneratorQr = () => {
this.props.navigation.navigate("GeneratorQr");
};
@action
actionDecreaseNum() {
this.Num--;
}
@action.bound
actionIncreaseNum() {
this.Num++;
}
decreaseNum = () => {
this.actionDecreaseNum();
};
increaseNum = () => {
this.actionIncreaseNum();
};
}
看源码,这个页面功能使用MobX比较简单。
生命周期
分析使用MobX
之后的react-native
的生命周期有什么变化!
由上图来说,如果项目中解注释生命周期方法shouldComponentUpdate
运行报错:
注释了这个方法之后,可以看到打印结果来看生命周期:
第一次执行结果,
点击按钮“数量增”,改变状态,看MobX这个时候会触发那些生命周期。
发现,MobX状态被改变之后,会再次触发生命周期方法componentWillUpdate
、componentDidUpdate
。也就是说,有没有方法shouldComponentUpdate
的区别!
从官方文档上解释
observer
observer
函数/装饰器可以用来将 React 组件转变成响应式组件。
observable 、action、computed、autorun、when
然后源码中第25、26行,@observable
装饰器可以在 ES7类属性中使用,将其转换成可观察的,即109行、111行方法中进行属性Num的处理。Num的变化会被立即观察到并更新。由此,我们可以看到点击数量增和数量减按钮,可以看到预期的效果。
现对源码稍作处理,在源码分析处理函数,actionDecreaseNum
和 actionIncreaseNum
再做运行点击,发现“数量递增”按钮不能使用了。为什么呢?了解一下Action注解关键字的作用先。
action
装饰器/函数遵循 javascript 中标准的绑定规则
它是一种动作,用来修改状态的动作。因此看到递减的按钮调用actionDecreaseNum
方法依然可以达到数量递减的效果,所以使用@action
它就充当了让方法具备使数字递减的动作。但是我们又能发现,即使不使用action也能实现递增和递减的效果,为什么要使用它呢?因为动作可以有助于更好的组织代码。而且官方还说,应该永远只对修改状态的函数使用动作。
回到未解决的问题上来,发现“数量递增”按钮不能使用了,为什么?
而且有趣的是,还有一个action.bound
。和action有什么区别?
数量递增按钮不能用了,原因是不能和箭头函数一起使用;箭头函数已经是绑定过的,已经绑定过的则不能重新绑定!
action和action.bound区别是,action.bound 可以用来自动地将动作绑定到目标对象。不需要一个name参数,名称将始终基于动作绑定的属性。如下图,
从官方文档上看,有几个关键字段
不要把 computed 和 autorun 搞混。它们都是响应式调用的表达式,但是,如果你想响应式的产生一个可以被其它 observer 使用的值,请使用 @computed,如果你不想产生一个新值,而想要达到一个效果,请使用 autorun。还有使用上的区别:
在生命周期方法componentDidMount
中加入响应方法autorun做监听
会发现,每次点击,且使Num状态发生变化之后,都会有立即响应打印"打印监听的Num数:xx"!但是,如果使用@computed
,
每次点击,即使Num状态发生变化,total方法中也不会有立即响应打印!除非UI中被调用。
而使用关键字when
的时候,会有立即响应。但是是有条件的,第一个参数中,要用到 @observable进行修饰 的属性变量。
比如图中,如果变量isVisiable
在构造方法中定义,即使调用并改变了某个别的使用 @observable进行修饰 的属性变量,也不会触发调用方法isVisableLog,除非,isVisiable使用@observable进行修饰。在when使用上,我们可以看到它可以起到一个简单拦截作用,在拦截中再做二次数据处理。
Provider、inject
使用关键字Provider
、inject
,实现下边的变化
看到没,UI内容中多了一个加载动画,加载动画文字下面的字体发生了变化。在源码中我做了如下处理:
在栈组件(app.js)之前新增了一个中间过渡的组件——MiddleWare.js
。过渡组件中引入了关键字Provider
——可以用来使用 React 的context机制来传递 store 给子组件。因此,我们可以在子组件中直接使用一些嵌入的属性。比如:引入 color={"#FFB90F"}
使用方式——this.props.color
;引入{...store}
使用方式——this.props.activeIndicator.showindicator()
。具体使用方式,往下瞅:
///MiddleWare.js
import React, { Component } from "react";
import { View, Text } from "react-native";
import { Provider } from "mobx-react";
import NewApp from "./app";
import store from "./src/base/Store";
export default class MiddleWare extends Component {
render() {
return (
<View style={{ flex: 1 }}>
<Provider color={"#FFB90F"} {...store}>
<NewApp />
</Provider>
</View>
);
}
}
其中,Store.js
中放入了一个加载动画
///Store.js
import activeIndicator from "./ActiveIndicator";
export default {
activeIndicator
};
加载动画组件:
配置上面都介绍过了,怎么使用?
使用点有两个,先注解引入,然后使用。
引入注解
使用注入的内容
然后就有上面显示的效果了,字体颜色能够修改,加载动画也在执行!
但是,使用过程中还是有些需要注意的。目前我发现的有俩!
第一, 由于我使用的mobx-react
版本,最新版本有bug,作者还没解决。所以需要返回到低一点的稳定版本使用。报错是,Expected a constructor
参考解决方案 最终修改下mobx-react
版本号。
react | react-native | react-navigation | react-native-gesture-handler | react-native-reanimated | mobx | mobx-react | @babel/plugin-proposal-decorators |
---|---|---|---|---|---|---|---|
“16.8.3” | “0.59.9” | “^3.11.1” | “^1.3.0” | “^1.1.0” | “^5.13.0” | “^5.4.3” | “^7.4.4” |
第二, 只针对color={"#FFB90F"}
这类属性。在使用Redux
的时候,写了<Provider color={"#FFB90F"} {...store}>
之后根本不用在使用的地方添加注解@inject("color", "activeIndicator")
来声明color。但是在MobX中不使用注解进行声明,调用this.props.color
还是没用的!
重点
我自定义一个组件,仅仅在生命周期componentDidMount
中进行打印一下普通内容
然后在HomePage.js
中通过注解符号 @ 引入,
运行结果,出乎意料,竟然执行到了那两行打印 :
Using HOC via es7 Decorators
对于高阶组件在这里使用,我多啰嗦两句,如果按照下面这样直接使用
你会发现一个奇怪的结果,当然我正是想要使用这个奇怪的结果,来解释高阶组件在这里是如何使用的!奇怪结果:"首页"白色标题文字和整个深橘色背景都没了。
我的猜测就是HomePage.js
通过添加注解@NetInfoDecorator
,使得由原本的继承方式class HomePage extends Component
改换成了 class HomePage extends NetInfoDecorator
。为了验证我的猜想,在NetInfoDecorator中加入配置标题的style
执行,结果和我的猜想是一样的
因此得出结论:HomePage.js
通过添加注解@NetInfoDecorator
,使得由原本的继承方式class HomePage extends Component
改换成了 class HomePage extends NetInfoDecorator
。并且适用于更广泛的应用,比如 我们可以在生命周期中预先去做统一的处理、监听网络连接、打印一些内容等。