React Native -- mobx

React Native -- mobx: 
  mobx是一個小巧優雅的庫,可以幫助我們很好的管理前端狀態的變更。
  其實mobx提供的不只是Component的自動更新,mobx本身提供的是一種自動更新的機制。
  ---------------------------------------------------------------------------------------------------------------------
  下面是看這個視頻(作者:天地之靈)記錄的筆記:http://v.youku.com/v_show/id_XMjQ4NTA4OTUwOA==.html?spm=a2hzp.8253869.0.0&from=y1.7-2#paction
  視頻中項目的GitHub地址:https://github.com/tdzl2003/mobx-lesson-20170122
  
  1)概念簡介 -- observable, computed, action, autorun, map
  2)入門 -- Counter
  3)入門 -- TODO List
  4)實戰 -- 登錄註冊(表單)
  5)實戰 -- 首頁(分頁列表)
  6)實戰 -- 購物車(聯動)
  7)高級 -- 計時器
  8)高級 -- autosave
  9)高級 -- Swiper優化
  
================================================================================
  安裝需要的依賴:mobx 和 mobx-react。
  npm i mobx mobx-react --save


  我們也要安裝一些 babel 插件,以支持 ES7 的 decorator 特性:
  npm i babel-plugin-transform-decorators-legacy babel-preset-react-native-stage-0 --save-dev


  現在,創建一個 .babelrc 文件配置 babel 插件:
{
 'presets': ['react-native'],
 'plugins': ['transform-decorators-legacy']
}
因爲我們編寫的是一個自定義 .babelrc 文件,所有只需要 react-native 的 preset。
配置 react-native 的 preset,還有指定一些先期運行的插件(我們這裏是 transform-decorators-legacy 插件)。
======================================================================================


*********** 1)概念簡介 demo1 -- observable的使用 ******************************
import { observable, autorun} from 'mobx';
export default function demo1() {
const value = observable(0);

autorun(() => {
console.log(`value is: ${value.get()}`);
});

value.set(2);
value.set(8);
value.set(-3);
}


*********** 1)概念簡介 demo2 -- computed的使用 ******************************
import { observable, computed, autorun} from 'mobx';
export default function demo2() {
const value = observable(0);

const condition = computed(() => {value.get() >= 0});

autorun(() => {
console.log(`condition is: ${condition.get()}`);
});

value.set(2);
value.set(8);
value.set(-3);
}


*********** 1)概念簡介 demo3 -- observable Object的使用 ******************************
import { observable, autorun} from 'mobx';
export default function demo3() {
// 返回一個對象
const value = observable({
foo: 0,
bar: 0,
get condition() {
return this.foo >= 0;
},
});

autorun(() => {
console.log(`value.foo is: ${value.foo}`);
});

autorun(() => {
console.log(`value.condition is: ${value.condition}`);
});

value.foo = 2;
value.foo = 8;
value.foo = -3;

value.bar = 1;
value.bar = 2;
}


*********** 1)概念簡介 demo4 -- observable Array的使用 ************
import { observable, computed, autorun} from 'mobx';
export default function demo4() {
const value = observable([0]);

autorun(() => {
console.log(`value.length is: ${value.length}`);
});

autorun(() => {
console.log(`value[0] is: ${value[0]}`);
});

value[0] = 1;
value.push(2);
value.push(3);

value.splice(0, 1);
}


*********** 1)概念簡介 demo5 -- use class and decorator,項目中多數用到這方面的知識,上面的demo只是簡單介紹 *****************
import { observable, computed, autorun, action, useStrict} from 'mobx';


useStrict(true); // 使用嚴格規範, 強制使用@action, 推薦寫法。


class Foo {
@observable
selected = 0;

@observable
items = [];

@computed
get selectedItem() {
if (this.selected >= this.items.length) {
return null;
}
return this.items[this.selected];
}

@action
addItem(item) {
this.items.push(item);
}

@action
removeAt(id) {
this.items.splice(id, 1);
if (this.selected >= id) {
this.selected--;
}
}

@action
removeSelected() {
this.items.splice(this.selected, 1);
}
}


export default function demo5() {
const foo = new Foo();

autorun(() => {
console.log(`Current selected is: ${foo.selectedItem()}`);
});

foo.addItem(0);
foo.addItem(1);
foo.addItem(2);
foo.addItem(3);

foo.selected = 2;

foo.removeSelected();

foo.removeAt(0);
}


*********** 1)概念簡介 demo6 -- observable map的使用,不常用 ***************
import { autorun, map } from 'mobx'; 


export default function demo6() {
const foo = map({});

autorun(() => {
console.log(`map have ${map.size} keys`);
});

foo.set('foo', 1);
foo.set('bar', 1);
foo.set('foo', 2);
foo.delete('bar');
}








*********** 2)入門 -- Counter ******************
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text,
} from 'react-native';


import { observable } from 'mobx';
import { observer } from 'mobx-react/native';


const counter = observable(0);


function inc() {
counter.set(counter.get() + 1);
}


function dec() {
counter.set(counter.get() - 1);
}


@observer
export default class Counter1 extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.value}>{counter.get()}</Text>
<Text style={styles.btn} onPress={inc}>+</Text>
<Text style={styles.btn} onPress={dec}>-</Text>
</View>
);
}
}


const styles = StyleSheet.create({
container: {

},

value: {

},

btn: {},


});


// 第二種counter,和上面的第一種不一樣,第一種的值會同時變化,第二種單獨變化。
@observer
export default class Counter2 extends Component {
@observable
counter = 0;

inc = () => {
++this.counter;
}

dec = () => {
--this.counter;
}

render() {
return (
<View style={styles.container}>
<Text style={styles.value}>{this.counter}</Text>
<Text style={styles.btn} onPress={this.inc}>+</Text>
<Text style={styles.btn} onPress={this.dec}>-</Text>
</View>
);
}
}


// 第三種把Text改爲TextInput,用戶可以輸入數字
@observer
export default class Counter3 extends Component {
@observable
counter = 0;

inc = () => {
++this.counter;
}

dec = () => {
--this.counter;
}

onChangeText = v => {
try{
this.counter = parseInt(v);
} catch(err) {

}
}

render() {
return (
<View style={styles.container}>
<TextInput style={styles.value} value={`${this.counter}`} onChangeText={this.onChangeText} />
<Text style={styles.btn} onPress={this.inc}>+</Text>
<Text style={styles.btn} onPress={this.dec}>-</Text>
</View>
);
}
}








********** 3)入門 -- TODO List, 自己改成ListView了,原作者用的是ScrollView **********************
import React, {Component, PropTypes,} from 'react';
import {
    StyleSheet,
    View,
    Text,
    ScrollView,
    TouchableOpacity,
    ListView,
    Alert,
} from 'react-native';


import {observable, action} from 'mobx';
import {observer} from 'mobx-react/native';


const titles = ['Eat', 'Drink', 'Think'];


class Todo {
    id = `${Date.now()}${Math.floor(Math.random() * 10)}`;


    @observable
    title = '';


    @observable
    done = false;


    constructor(title) {
        this.title = title;
    }
}


function randomTodoTitle() {
    return titles[Math.floor(Math.random() * titles.length)];
}


@observer
class TodoItem extends Component {
    static propTypes = {
        data: PropTypes.instanceOf(Todo),
    };


    @action
    onPress = () => {
        const {data} = this.props;
        data.done = !data.done;
    };


    render() {
        const {data} = this.props;
        return (
            <Text
                style={[styles.item, data.done && styles.done]}
                onPress={this.onPress}
            >
                {data.title}
            </Text>
        );
    }
}


@observer
export default class MobxDemoTodoList extends Component {


    static title = '3 - TodoList';


    renderItem(data) {
        return (<TodoItem key={data.id} data={data}/>);
    }


    render() {
        return (
            <ListView dataSource={this.state.dataSource}
                      renderRow={(data, non, sid) => { return this.renderItem(data)}}/>
        );
    }


    constructor(props) {
        super(props);
        const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
        this.state = {
            dataSource: ds.cloneWithRows([
                new Todo('Eat'),
                new Todo('D'),
                new Todo('T'),
                new Todo('Eat'),
                new Todo('D'),
                new Todo('T'),
                new Todo('Eat'),
                new Todo('D'),
                new Todo('T'),
                new Todo('Eat'),
                new Todo('D'),
            ])
        };
    }
}


const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: 'white',
    },


    content: {
        justifyContent: 'center',
        alignItems: 'center',
    },


    todo: {
        fontSize: 20,
    },


    item: {
        fontSize: 30,
        margin: 20,
        color: '#f00',
    },


    done: {
        fontSize: 30,
        margin: 20,
        color: '#ccc',
        textDecorationLine: 'line-through', // 一條橫線從文本中間穿過
    },
});






********** 4)實戰 -- 登錄註冊(表單) **********************
import validate from 'mobx-form-validate';
import { observable, toJS } from 'mobx';


// 自己定義的component
import { FormProvider, FormItem, Submit } from './components/form';


/*
// 第1種,不用@validate
class LoginForm {
@observable
mobile = '';

@observable
pwd = '';

@computed
get mobileValidateError() {
return /^1\d{10}$/.test(this.mobile) ? null : 'Please input a valid phone number.';
}

@computed
get pwdValidateError() {
return /^.+$/.test(this.pwd) ? null : 'Please input any password.';
}

@computed
get validateError() {
return this.mobileValidateError || this.pwdValidateError;
}

@computed
get isValid() {
return !this.validateError;
}
} */


// 第2種
class LoginForm {
@observable
@validate(/^1\d{10}$/, 'Please input a valid phone number.')
mobile = '';

@observable
@validate(/^.+$/, 'Please input any password.')
pwd = '';

submit = async () => {
// await post('/login', toJS(this));
alert(JSON.stringify(toJS(this)));
}
}


export default class LoginPage extends Component {
static title = '4 - Login Form';
form = new LoginForm();
render() {
return (
<FormProvider form={this.form}>
<View style={styles.container}>
<FormItem name="mobile" underlineColorAndroid="transparent">Mobile<FormItem>
<FormItem secureTextEntry name="pwd">Password<FormItem>
<Submit onSubmit={this.form.submit}>Login</Submit>
</View>
</FormProvider>
);
}
}


@observer
export default class FormItem extends Component {
  static propTypes = {
    name: PropTypes.string.isRequired,
    form: PropTypes.object,
    children: PropTypes.string.isRequired,
    autoFocus: PropTypes.boolean,


    ...TextInput.propTypes,
  };
  static contextTypes = {
    form: PropTypes.object,
  };
  state = {
    focused: this.props.autoFocus,
  };
  onChangeText = (text) => {
    const { name } = this.props;
    const form = this.context.form || this.props.form;
    form[name] = text;
  };
  onFocus = () => {
    if (!this.state.focused) {
      this.setState({ focused: true });
    }
  };
  render() {
    const { name, children, form: _, ...others } = this.props;
    const { focused } = this.state;
    const form = this.context.form || this.props.form;


    return (
      <View style={styles.container}>
        <View style={styles.row}>
          <Text style={styles.label}>{children}</Text>
          <View style={styles.inputWrapper}>
            <TextInput
              {...others}
              onFocus={this.onFocus}
              value={form[name]}
              onChangeText={this.onChangeText}
              style={styles.input}
            />
          </View>
        </View>
        <View>
          {focused && <Text style={styles.error}>{form[camelCase('validateError', name)]}</Text>}
        </View>
      </View>
    );
  }
}


export default class FormProvider extends Component {
  static propTypes = {
    // eslint-disable-next-line react/forbid-prop-types
    form: PropTypes.object,
    children: PropTypes.element.isRequired,
  };


  static childContextTypes = {
    form: PropTypes.object,
  };


  getChildContext() {
    return {
      form: this.props.form,
    };
  }
  render() {
    return React.Children.only(this.props.children);
  }
}


@observer
export default class Submit extends Component {
  static propTypes = {
    children: PropTypes.string.isRequired,
    form: PropTypes.object,
    onSubmit: PropTypes.func,
  };
  static contextTypes = {
    form: PropTypes.object,
  };
  render() {
    const { children, onSubmit } = this.props;
    const form = this.context.form || this.props.form;
    return (
      <TouchableOpacity
        style={[styles.button, form.isValid && styles.active]}
        disabled={!form.isValid}
        onPress={onSubmit}
      >
        <Text>{children}</Text>
      </TouchableOpacity>
    );
  }
}




********** 5)實戰 -- 首頁(分頁列表) **********************

沒記筆記


********** 6)實戰 -- 購物車(聯動) **********************

沒記筆記


********** 7)高級 -- 計時器 **********************

沒記筆記


********** 8)高級 -- autosave **********************

沒記筆記


********** 9)高級 -- Swiper優化 **********************

沒記筆記



  



mobx與redux比較


1.視圖更新
mobx:按需更新
redux:自上而下


2.列表支持
mobx:良好
redux:很差


3.代碼量
mobx:少
redux:多


4.安全性
mobx:一般
redux:良好


5.嚴謹性
mobx:自由
redux:嚴謹


6.性能
mobx:高
redux:中等,特定場景低下


7.插件
mobx:不多、一般不用
redux:豐富、選擇困難


8.異步操作
mobx:方便
redux:方便


9.主動通知
mobx:不方便
redux:部分場景不方便


10.服務端渲染
mobx:不支持
redux:支持,但有毒


11.自動化測試
mobx:一般
redux:極爲方便


12.數據可回溯
mobx:不可
redux:可


13.用途
mobx:廣泛,偏重數據&局部狀態
redux:有毒,偏重全局性


14.適用範疇
mobx:快速迭代的項目,中小團隊、分工不細、表單較多的項目

redux:大型、持續較久的項目,分工細緻的團隊,編輯器等涉及數據回溯的功能









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章