MobX 在 React Native开发中的应用

MobX 是一款精准的状态管理工具库,如果你在 React 和 React Native 应用中使用过 Flux、Alt、Redux 和 Reflux,那毫不犹豫地说,MobX 的简单性将成为你状态管理的不二之选。

加入我们要实现这样一个功能:创建一个新的列表,向列表中加入新的条目并刷新,这就用到了MobX的状态管理。

环境配置

首先,我们为MobX配置相关的环境支持。

1.npm i mobx mobx-react --save //引入mobx
2.npm i babel-plugin-transform-decorators-legacy babel-preset-react-native-stage-0 --save-dev //能够使用@标签
3.在项目目录下找到.babelrc文件,并修改为{
"presets": ["react-native"],
"plugins": ["transform-decorators-legacy"]
}

现在我们项目配置好了,可以写代码了。不过在开发之前需要对

mobx标签

mobx常用的标签做一个解释。

  • @observable: 使用此标签监控要检测的数据;
  • @observer: 使用此标签监控当数据变化是要更新的Component(组件类)
  • @action:使用此标签监控数据改变的自定义方法(当在需要数据改变的时候执行此自定义方法,那么View层也会跟着自动变化,默认此View层已经使用@observer标签监控)

mobx实例1

下面实现一个使用mobx实现一个简单的数据复制更新功能。

import React, {Component} from 'react';
import {
    View,
    StyleSheet,
    ScrollView,
    Text,
} from 'react-native';

/*
 * 引入头文件
 * */
import {observable, action} from 'mobx';
import {observer} from 'mobx-react/native';

/*
* 添加数据
* */
const datas = [
    {name:'苹果',count:0},
    {name:'梨',count:0},
    {name:'香蕉',count:0},
    {name:'草莓',count:0},
    {name:'橘子',count:0},
];

/*
* 对整个列表添加观察,观察列表个数的变化
* */
@observer
export default class MobxTestSecond extends Component {

    /*
    * 数据管理器
    * */
    dataManager = new DataSource();

    componentWillMount() {
        /*
        * 赋值初始数据
        * */
        this.dataManager.replace(datas);
    }

    /*
    * 添加一个新的Item
    * */
    addItem = () => {
       let item = {name:'西瓜',count:0};
        this.dataManager.addItem(item)
    };

    /*
    * 删除第一个Item
    * */
    deleteItem = () => {
        this.dataManager.deleteItem(0);
    };

    render() {
        return (
        <View style={styles.container}>
            <View style={styles.addItemView}>
                <Text style={styles.addItem} onPress={this.addItem}>增加</Text>
                <Text style={styles.addItem} onPress={this.deleteItem}>删除</Text>
            </View>
            <ScrollView>
                {
                    this.dataManager.dataSource.slice(0).map((item,i)=> <ItemView key = {i} item = {item}/>)
                }
            </ScrollView>
        </View>
        );
    }
}



/*
* 对每一个Item添加观察,改变个数
* */
@observer
class ItemView extends Component {

    countAdd = () => {
        this.props.item.add();
    };

    countDec = () => {
        this.props.item.dec();
    };

    render() {
        const {item} = this.props;
        return (
            <View style={styles.itemContainer}>
                <Text>{item.name}</Text>
                <Text>{item.count}</Text>
                <Text style={styles.btn} onPress={this.countAdd}> + </Text>
                <Text style={styles.btn} onPress={this.countDec}> - </Text>
            </View>
        );
    }
}



/*
 * 整个列表页数据管理器
 * */
class DataSource {
    // 本地数据源
    @observable
    dataSource = [];

    // 添加初始数据
    @action
    replace = (items) => {
        // 1. 清空原数据
        this.dataSource.splice(0, this.dataSource.length);

        // 2. 加载新数据
        items.map((item, i) => {
            this.dataSource.push(new Item(item));
        });
    };

    // 添加新数据
    @action
    addItem = (item) => {
        this.dataSource.unshift(new Item(item));
    };


    // 删除一条数据
    @action
    deleteItem = (idx) => {
        this.dataSource.splice(idx, 1);
    };
}
/*
 * 单条Item数据管理器
 * */
class Item {

    /*
    * 商品名称(此值是不变的所以不需要检测此值)
    * */
    name;

    /*
    * 监控商品个数
    * */
    @observable
    count;


    constructor(item) {
        this.name = item.name;
        this.count = item.count;
    };

    /*
    * 商品个数+1
    * */
    @action
    add = () => {
        this.count += 1;
    };

    /*
    * 商品个数-1
    * */
    @action
    dec= () => {
        this.count > 0 && (this.count -= 1);
    };
}

案例2

新建一个 listStore.js文件。

  1. 从 mobx 导入 observable – observable 可以给存在的数据结构如对象、数组和类增加可观察的能力。简单地给类属性增加一个 @observable 装饰器(下一代 ECMAScript),或者调用 observable 或 extendObservable 函数(ES5);
  2. 创建一个叫做 ObservableListStore 的类;
  3. 创建一个可观察的数组 list;
  4. 创建三个操作列表数组的方法;
  5. 创建一个 ObservableListStore 的实例 observableListStore;
  6. 导出 observableListStore
import {observable} from 'mobx'

let index = 0

class ObservableListStore {
  @observable list = []

  addListItem (item) {
    this.list.push({
      name: item, 
      items: [],
      index
    })
    index++
  }

  removeListItem (item) {
    this.list = this.list.filter((l) => {
      return l.index !== item.index
    })
  }

  addItem(item, name) {
    this.list.forEach((l) => {
      if (l.index === item.index) {
        l.items.push(name)
      }
    })
  }
}

const observableListStore = new ObservableListStore()
export default observableListStore

现在已经用了存储器,我们修改项目的入口文件,使用存储,创建导航。

import React, { Component } from 'react'
import App from './app/App'
import ListStore from './app/mobx/listStore'

import {
  AppRegistry,
  Navigator
} from 'react-native'

class ReactNativeMobX extends Component {
  renderScene (route, navigator) {
    return <route.component {...route.passProps} navigator={navigator} />
  }
  configureScene (route, routeStack) {
    if (route.type === 'Modal') {
      return Navigator.SceneConfigs.FloatFromBottom
    }
    return Navigator.SceneConfigs.PushFromRight
  }
  render () {
    return (
      <Navigator
        configureScene={this.configureScene.bind(this)}
        renderScene={this.renderScene.bind(this)}
        initialRoute={{
          component: App,
          passProps: {
            store: ListStore
          }
        }} />
    )
  }
}

AppRegistry.registerComponent('ReactNativeMobX', () => ReactNativeMobX)

现在,我们来创建应用组件。实现对数据的操作。

import React, { Component } from 'react'
import { View, Text, TextInput, TouchableHighlight, StyleSheet } from 'react-native'
import {observer} from 'mobx-react/native'
import NewItem from './NewItem'

@observer
class TodoList extends Component {
  constructor () {
    super()
    this.state = {
      text: '',
      showInput: false
    }
  }
  toggleInput () {
    this.setState({ showInput: !this.state.showInput })
  }
  addListItem () {
    this.props.store.addListItem(this.state.text)
    this.setState({
      text: '',
      showInput: !this.state.showInput
    })
  }
  removeListItem (item) {
    this.props.store.removeListItem(item)
  }
  updateText (text) {
    this.setState({text})
  }
  addItemToList (item) {
    this.props.navigator.push({
      component: NewItem,
      type: 'Modal',
      passProps: {
        item,
        store: this.props.store
      }
    })
  }
  render() {
    const { showInput } = this.state
    const { list } = this.props.store
    return (
      <View style={{flex:1}}>
        <View style={styles.heading}>
          <Text style={styles.headingText}>My List App</Text>
        </View>
        {!list.length ? <NoList /> : null}
        <View style={{flex:1}}>
          {list.map((l, i) => {
            return <View key={i} style={styles.itemContainer}>
              <Text
                style={styles.item}
                onPress={this.addItemToList.bind(this, l)}>{l.name.toUpperCase()}</Text>
              <Text
                style={styles.deleteItem}
                onPress={this.removeListItem.bind(this, l)}>Remove</Text>
            </View>
          })}
        </View>
        <TouchableHighlight
          underlayColor='transparent'
          onPress={
            this.state.text === '' ? this.toggleInput.bind(this)
            : this.addListItem.bind(this, this.state.text)
          }
          style={styles.button}>
          <Text style={styles.buttonText}>
            {this.state.text === '' && '+ New List'}
            {this.state.text !== '' && '+ Add New List Item'}
          </Text>
        </TouchableHighlight>
        {showInput && <TextInput
          style={styles.input}
          onChangeText={(text) => this.updateText(text)} />}
      </View>
    );
  }
}

const NoList = () => (
  <View style={styles.noList}>
    <Text style={styles.noListText}>No List, Add List To Get Started</Text>
  </View>
)

const styles = StyleSheet.create({
  itemContainer: {
    borderBottomWidth: 1,
    borderBottomColor: '#ededed',
    flexDirection: 'row'
  },
  item: {
    color: '#156e9a',
    fontSize: 18,
    flex: 3,
    padding: 20
  },
  deleteItem: {
    flex: 1,
    padding: 20,
    color: '#a3a3a3',
    fontWeight: 'bold',
    marginTop: 3
  },
  button: {
    height: 70,
    justifyContent: 'center',
    alignItems: 'center',
    borderTopWidth: 1,
    borderTopColor: '#156e9a'
  },
  buttonText: {
    color: '#156e9a',
    fontWeight: 'bold'
  },
  heading: {
    height: 80,
    justifyContent: 'center',
    alignItems: 'center',
    borderBottomWidth: 1,
    borderBottomColor: '#156e9a'
  },
  headingText: {
    color: '#156e9a',
    fontWeight: 'bold'
  },
  input: {
    height: 70,
    backgroundColor: '#f2f2f2',
    padding: 20,
    color: '#156e9a'
  },
  noList: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  noListText: {
    fontSize: 22,
    color: '#156e9a'
  },
})

export default TodoList
  1. 从 mobx-react/native 导入 observer;
  2. 使用 @observer 装饰器描述类,确保相关数组变化后组件独立地重渲染;
  3. 导入已经创建好的组件 NewItem。这是我们要增加新条目时转向的组件;
  4. 在 addListItem中,把 this.state.text 传入this.props.store.addListItem。在与输入框绑定的 updateText 中会更新this.state.text;
  5. 在 removeListItem 中调用 this.props.store.removeListItem 并传入条目;
  6. 在 addItemToList 中调用 this.props.navigator.push,传入条目和数组存储两个参数;
  7. 在 render 方法中,通过属性解构数据存储:
const { list } = this.props.store

8.在 render 方法中,也创建了界面,并绑定了类的方法

import React, { Component } from 'react'
import { View, Text, StyleSheet, TextInput, TouchableHighlight } from 'react-native'

class NewItem extends Component {
  constructor (props) {
    super(props)
    this.state = {
      newItem: ''
    }
  }
  addItem () {
    if (this.state.newItem === '') return
    this.props.store.addItem(this.props.item, this.state.newItem)
    this.setState({
      newItem: ''
    })
  }
  updateNewItem (text) {
    this.setState({
      newItem: text
    })
  }
  render () {
    const { item } = this.props
    return (
      <View style={{flex: 1}}>
        <View style={styles.heading}>
          <Text style={styles.headingText}>{item.name}</Text>
          <Text
            onPress={this.props.navigator.pop}
            style={styles.closeButton}>×</Text>
        </View>
        {!item.items.length && <NoItems />}
        {item.items.length ? <Items items={item.items} /> : <View />}
        <View style={{flexDirection: 'row'}}>
          <TextInput
            value={this.state.newItem}
            onChangeText={(text) => this.updateNewItem(text)}
            style={styles.input} />
          <TouchableHighlight
            onPress={this.addItem.bind(this)}
            style={styles.button}>
            <Text>Add</Text>
          </TouchableHighlight>
        </View>
      </View>
    )
  }
}

const NoItems = () => (
  <View style={styles.noItem}>
    <Text style={styles.noItemText}>No Items, Add Items To Get Started</Text>
  </View>
)
const Items = ({items}) => (
  <View style={{flex: 1, paddingTop: 10}}>
   {items.map((item, i) => {
        return <Text style={styles.item} key={i}>• {item}</Text>
      })
    }
  </View>
)

const styles = StyleSheet.create({
  heading: {
    height: 80,
    justifyContent: 'center',
    alignItems: 'center',
    borderBottomWidth: 1,
    borderBottomColor: '#156e9a'
  },
  headingText: {
    color: '#156e9a',
    fontWeight: 'bold'
  },
  input: {
    height: 70,
    backgroundColor: '#ededed',
    padding: 20,
    flex: 1
  },
  button: {
    width: 70,
    height: 70,
    justifyContent: 'center',
    alignItems: 'center',
    borderTopWidth: 1,
    borderColor: '#ededed'
  },
  closeButton: {
    position: 'absolute',
    right: 17,
    top: 18,
    fontSize: 36
  },
  noItem: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  noItemText: {
    fontSize: 22,
    color: '#156e9a'
  },
  item: {
    color: '#156e9a',
    padding: 10,
    fontSize: 20,
    paddingLeft: 20
  }
})

export default NewItem

如果你之前使用过MobX,那么相信在React Native使用同样简单。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏林德熙的博客

WPF 修改图片颜色

在 WPF 可以使用很多图片处理的方法,本文告诉大家的是一个图片处理,可以把处理的图片保存在文件。

3461
来自专栏221-B

不懂汇编,如何逆向(iOS)

其实这个是非必要项, 自己手动砸壳需要已越狱的手机. 想手动砸壳可以参考这篇文章.

3122
来自专栏Charlie's Road

Container ViewController自定义转场控制器。

最近接触到新公司的老项目改版。自从来了之后一直在忙另一个项目,也没有看老项目的实现逻辑。 看到设计稿的时候,并不是普通的树形标签导航的样子。大致效果如FaceU...

1251
来自专栏Material Design组件

Human Interface Guidelines —— Pickers

1012
来自专栏酷玩时刻

微信公众号开发之自定义菜单

在Jfinal-weixin中有封装菜单的创建、查询、删除、以及个性化菜单的创建、查询、删除、测试个性化菜单匹配结果

1242
来自专栏Flutter&Dart

Flutter之drawer详细分析(你要的操作都有)

可以看到,根据我们对drawer的认识,并不是想要的结果,所以这个drawer并不完整,然后我们继续添加代码,修改drawer

2052
来自专栏葡萄城控件技术团队

VS2010 Extension实践(1)

最近VS2010 Extension在Visual Studio Blog(http://blogs.msdn.com/visualstudio/)上提得很频繁...

2069
来自专栏我有一个梦想

Python 项目实践一(外星人入侵小游戏)第三篇

接着上节的继续学习, 一 重构:模块game_functions 在大型项目中,经常需要在添加新代码前重构既有代码。重构旨在简化既有代码的结构,使其更容易扩展。...

3039
来自专栏携程技术中心

干货 | React Fiber 初探

1792
来自专栏哲学驱动设计

OEA 中 WPF 树型表格整体重构

为什么要重构     上两个月主要做了一件事情,那就是把 OEA 框架中的 TreeGrid 控件,从结构上重新设计,并大量重构现有代码。而花较大精力做这件事的...

2056

扫码关注云+社区

领取腾讯云代金券