记录一下在学习React-Native所需要的技能.
在用mobx之前一定要对mobx 有一些初步了解,慢慢去敲代码,才能发现这个东西的妙处!
什么是mobx?
mobx:一种状态监听.在我理解,例如,它是监听对象里的某一个属性.如果被监听的对象属性发生了改变,会触发对应的render(重新渲染).这种监听机制类似于iOS的KVC.
为什么用mobx不用state?
mobx 会render对应的子页面,而且状态管理一目了然,对于状态多的界面mobx性能更优,状态管理更方便.而state 会渲染整个父页面,如果状态多了.
运用 mobx :
1.首先导入mobx 库
"dependencies": {
"mobx": "^3.1.9",
"mobx-react": "^4.1.8",
},
"devDependencies": {
"babel-plugin-transform-decorators-legacy": "^1.3.4"
}
babel-plugin-transform-decorators-legacy
ES7的装饰器模式插件,也就是@observable
这种,同时需要在package.json同级目录下创建一个.babelrc
文件,文件里的内容如下
"presets": ["react-native"],
"plugins": [
"syntax-decorators",
"transform-decorators-legacy"
]
}
在package.json里对应写入,然后npm install
mobx
几种关键字:
网上搜索了很多,发现不好理解,下面是我自己的理解
observable
:监听的对应属性
computed
:根据监听属性,计算出来的新值
action
:对监听属性的对应操作
runInAction
:在action
里,在一个loop event里返回处理属性(意思等等被监听属性修改完,一起返回);
computed
和 runInAction
可以没有,剩下两个在没有 你也没法监听了
举例:运用在列表请求里,对应标注↓
<1>请求一个列表
//导入
import {observable, computed, action, runInAction} from 'mobx';
export default class HJNetListUtil{
@observable listData = [];//监听的数组
@observable errorMsg = ''//监听的错误信息
@observable page = 1 //监听的上下拉页数
@observable loading = false //监听是否正在请求
@observable isMore = true //是否是最后一页
@observable refreshing = false; //是否正在刷新
//构造函数,比如new HJNetListUtil(),对应传的参数
constructor(url,params,listKey) {
url = gBaseUrl.baseUrl + url;//拼接URL baseUrl是域名, url是链接
this.listKey = listKey;//对应的解析字段(比如list:[],data:[]看你们服务器返回的是什么)
this.url = url;
this.params = params;
this.params['page'] = this.page;
this.POST();
}
@action //对应的POST 请求 用了action
POST(params){
if (!this.loading) {
this.loading = true;
if (params) {
this.params = params;
this.params['page'] = this.page;
}
if (this.page ==1 ) {this.refreshing = true}
NetUtil.POST(
this.url,
this.params,
(data)=>this.successCallback(data),
(error)=>this.failCallback(error)
);
}
}
successCallback(data){
this.errorMsg = '';
this.refreshing = false;
var list = [];
if (this.listKey) {
list = data.data[listKey];
}else{
list = data.data.list;
}
//这个地方可以用@computed 计算list.length的长度
if (!list.length) {
this.isMore = false;
this.loading = false;
return;
}
if (this.page == 1) {
this.listData.replace(list);//第一页刷新
}else{
this.listData.push(...list);//拼接数组
}
this.loading = false;
}
failCallback(error){
this.loading = false;
this.errorMsg = error;
}
}
对应的控件↓,我将没用的删除掉
'use strict';
import {observer} from 'mobx-react/native';
@observer
export default class HomeCategoryList extends React.Component {
static defaultProps = {
listStore:Object,
};
render() {
const {listData,loading,refreshing} = this.props.listStore;
return (
<View style={styles.containStyle}>
<FlatList
numColumns={2}
data={listData.slice()}
renderItem={this._renderItem}
ItemSeparatorComponent={this._renderSeparator}
ListFooterComponent={this._renderFooter}
onEndReached={()=>this._onEndReach()}
onEndReachedThreshold={0.1}
onRefresh={()=>this._onRefresh()}
refreshing={refreshing}
keyExtractor={(item, index) => index}
/>
<Loading isShow={this.props.listStore.isMore && loading} />
</View>
);
}
1.import {observer} from 'mobx-react/native';
导入
2.@observer
监听的类
3.const {listData,loading,refreshing} = this.props.listStore;
从对象中取出对应被监听的属性,这时只要属性一变,就会重新渲染该控件,this.props.listStore
是父控件传过来的,如果是本类应该写
var params = {
keyWord:'',
}
this.listStore = new HJNetListUtil('url',params);
不过new HJNetListUtil
一定要在render 之前创建,一般写在constructor ()
函数里.
4.listData.slice()
数组拷贝,如果不写拷贝,那么数组即使变化也不会触发控件render.(应该是地址没变就不会触发render)
<2>.滑动条,选项卡,最好用第二种方法
对应的界面应该这样写
'use strict';
import {observable, computed, action, runInAction,autorun} from 'mobx';
import React,{Component} from 'react';
import {observer} from 'mobx-react/native';
import {
AppRegistry,
StyleSheet,
Text,
View,
Alert,
Image,
TouchableOpacity,
ListView
} from 'react-native';
//待办事项行数据
class TodoListItem {
index;
@observable
title;
@observable
seleted = false;
@action
toggleFinish() {
if (!this.seleted) {this.seleted = true}
}
}
//待办事项列表数据
class TodoListHolder {
@observable
dataList = [];
@computed
get taskLeft() {
return this.dataList.filter((it) => it.seleted == true);
}
@action
clear(){}
}
@observer
export default class HomeSlider extends React.Component {
todoList = new TodoListHolder();
static defaultProps = {
list :[
'John', 'Joel', 'James', 'Jimmy', 'Jackson', 'Jillian', 'Julie', 'Devin'
]
};
ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
pre = 0;
// 初始化模拟数据
constructor(props) {
super(props);
for (let i = 0; i < this.props.list.length; i++) {
let listItem = new TodoListItem();
if (i == 0) {listItem.seleted = true}
listItem.title = this.props.list[i];
listItem.index = i;
this.todoList.dataList.push(listItem)
}
autorun(() => {
var arr = this.todoList.dataList.filter((it) => it.seleted == true);
var c = 0;
if (arr.length>1)
{
// console.log('保持的',this.pre);
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
if (item.index === this.pre) {
// console.log('上一个',this.pre);
this.todoList.dataList[this.pre].seleted = false;
}else{
c = item.index;
// console.log('现在的',this.pre);
}
}
this.pre = c;
// console.log('-----------')
}
})
};
render() {
return (
<View style={styles.containStyle}>
<ListView
dataSource={this.ds.cloneWithRows(this.todoList.dataList.slice())}
renderRow={this.renderRow.bind(this)}
horizontal={true}
showsHorizontalScrollIndicator={false}
/>
</View>
);
}
renderRow(rowData,sectionID,rowID){
return(
<Item rowData={rowData}/>
)
}
}
@observer
export class Item extends React.Component {
render(){
return(
<TouchableOpacity activeOpacity={0.5} onPress={()=>this.props.rowData.toggleFinish()}>
{this.renderItem()}
</TouchableOpacity>
)
}
renderItem(){
if (this.props.rowData.seleted) {
return(
<View style={styles.topClickStyle_S}>
<Text style={styles.topTextStyle}>{this.props.rowData.title}</Text>
</View>
)
}else {
return(
<View style={styles.topClickStyle}>
<Text>{this.props.rowData.title}</Text>
</View>
)
}
}
}
const styles = StyleSheet.create({
containStyle:{
flex:1,
marginLeft:10,
marginRight:10,
},
SeparatorComponent:{
height:10,
backgroundColor:'#dddddd'
},
topClickStyle:{
height:30,
width:60,
marginRight:5,
backgroundColor:'white',
justifyContent:'center',
alignItems:'center',
},
topClickStyle_S:{
height:30,
width:60,
marginRight:5,
backgroundColor:'white',
justifyContent:'center',
alignItems:'center',
borderBottomWidth:2,
borderBottomColor:'red',
},
topTextStyle:{
color:'red',
}
})
监听属性改变会触发autorun
.在这里可以做很多事情
第二种方法,比较简单
'use strict';
import {observable, computed, action, runInAction,autorun} from 'mobx';
import React,{Component} from 'react';
import {observer} from 'mobx-react/native';
import {
AppRegistry,
StyleSheet,
Text,
View,
Alert,
Image,
TouchableOpacity,
ListView
} from 'react-native';
//待办事项行数据
class TodoListItem {
redModel = null;
constructor(redModel) {
this.redModel = redModel;
}
@action
select=()=>{
if (this.redModel.selectedItem == this) {
this.redModel.selectedItem = null;
}else {
this.redModel.selectedItem = this
}
}
@computed
get seleted(){
return this.redModel.selectedItem === this;
}
title;
}
//待办事项列表数据
class TodoListHolder {
@observable
selectedItem=null;
@observable
dataList = [];
}
@observer
export default class Slider extends React.Component {
todoList = new TodoListHolder();
constructor(props) {
super(props);
for (let i = 1; i < 30; i++) {
let listItem = new TodoListItem(this.todoList);
listItem.title = `待办事项${i}`;
this.todoList.dataList.push(listItem)
}
this.todoList.selectedItem = this.todoList.dataList[0];
}
ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => {
return r1 !== r2
}
});
render() {
return (
<View style={styles.containStyle}>
<ListView
dataSource={this.ds.cloneWithRows(this.todoList.dataList.slice())}
renderRow={this.renderRow.bind(this)}
horizontal={true}
showsHorizontalScrollIndicator={false}
/>
</View>
);
}
renderRow(rowData,sectionID,rowID){
return(
<Item rowData={rowData}/>
)
}
componentDidMount() {
}
}
@observer
export class Item extends React.Component {
render(){
return(
<TouchableOpacity activeOpacity={0.5} onPress={()=>this.props.rowData.select()}>
{this.renderItem()}
</TouchableOpacity>
)
}
renderItem(){
if (this.props.rowData.seleted) {
return(
<View style={styles.topClickStyle_S}>
<Text style={styles.topTextStyle}>{this.props.rowData.title}</Text>
</View>
)
}else {
return(
<View style={styles.topClickStyle}>
<Text>{this.props.rowData.title}</Text>
</View>
)
}
}
}
const styles = StyleSheet.create({
containStyle:{
flex:1,
marginLeft:10,
marginRight:10,
},
SeparatorComponent:{
height:10,
backgroundColor:'#dddddd'
},
topClickStyle:{
height:30,
marginRight:15,
backgroundColor:'white',
justifyContent:'center',
alignItems:'center',
},
topClickStyle_S:{
height:30,
marginRight:15,
backgroundColor:'white',
justifyContent:'center',
alignItems:'center',
borderBottomWidth:2,
borderBottomColor:'red',
},
topTextStyle:{
color:'red',
}
})
总结
仔细看会发现其实主要就这几步
1.定义一个对象,在对象里监听你想监听的属性(@observable 属性
)
2.@observer对应的控件,在render() 里将你监听的属性取出来
3.然后赋值到控件上,赋值数组的时候要注意拷贝