React Native 中使用Mobx實踐

1、爲何咱們須要使用Mobx?

  • Mobx 是 flux 實現的後起之秀. 以更簡單的時候和更少的概念, 讓 flux 使用起來變得更簡單.
  • 相比 Redux 有mutation, action, dispatch 等概念. Mobx則更加簡潔, 更符合對Store 增刪改查的操做概念.
  • Mobx是學習成本更低,性能更好的狀態解決方案。
  • Mobx代碼量少,與TS結合性更好,可使用更少的代碼實現更復雜的頁面
  • 使用好Mobx能夠極大的下降頁面的沒必要要的重繪次數

2、React Native工程集成Mobx以及TS環境的搭建

(1) 安裝Mobx、Mobx-React

npm i mobx mobx-react
複製代碼

(2)安裝TS

npm i typescript
複製代碼

(3)安裝TS BaBle解析器

在package.json中輸入以下依賴,並從新執行npm installhtml

"devDependencies": {
    "@babel/core": "^7.5.5",
    "@babel/plugin-proposal-decorators": "^7.4.4",
    "@babel/preset-typescript": "^7.3.3",
    "@babel/runtime": "^7.5.5",
    "@types/react-dom": "^16.8.5",
    "@types/react": "^16.9.2",
    "@types/react-native": "^0.60.7",
    "babel-jest": "^24.9.0",
    "babel-plugin-transform-class-properties": "^6.24.1",
    "jest": "^24.9.0",
    "metro-react-native-babel-preset": "^0.56.0",
    "react-test-renderer": "16.8.6"
  },
複製代碼

(4)配置Bable插件

在.babelrc文件中應用如下插件腳本,如沒有則在工程根目錄新建java

{
  "presets": [
    "@babel/preset-typescript",
    [
      "module:metro-react-native-babel-preset"
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ],
    "transform-class-properties"
  ]
}
複製代碼

(5)配置TS校驗規則

在tsconfig.json文件中應用如下校驗配置,如沒有則在工程根目錄新建node

{
  "compilerOptions": {
    "target": "es2017",
    "module": "commonjs",
    "jsx": "preserve",
    "strict": true,
    "noImplicitAny": false,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "esModuleInterop": true,
    "removeComments": false,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "allowJs": true,
    "checkJs": true
  },
  "exclude": [
    "node_modules"
  ],
  "types": [
    "react",
    "react-dom",
    "react-native"
  ]
}
複製代碼

具體字段含義參照www.html.cn/doc/typescr…react

3、Mobx中一些經常使用的概念

(1)observable和autorun

import { observable, autorun } from 'mobx';

const value = observable(0);
const number = observable(100);

autorun(() => {
  console.log(value.get());
});

value.set(1);
value.set(2);
number.set(101);

複製代碼

能夠看到,控制檯中依次輸出0,1,2。 observable能夠用來觀測一個數據,這個數據能夠數字、字符串、數組、對象等類型(相關知識點具體會在後文中詳述),而當觀測到的數據發生變化的時候,若是變化的值處在autorun中,那麼autorun就會自動執行。 上例中的autorun函數中,只對value值進行了操做,而並無number值的什麼事兒,因此number.set(101)這步並不會觸發autorun,只有value的變化才觸發了autorun。typescript

(2)計算屬性——computed

假如如今咱們一個數字,但咱們對它的值不感興趣,而只關心這個數組是否爲正數。這個時候咱們就能夠用到computed這個屬性了。npm

const number = observable(10);
const plus = computed(() => number.get() > 0);

autorun(() => {
  console.log(plus.get());
});

number.set(-19);
number.set(-1);
number.set(1);
複製代碼

依次輸出了true,false,true。 第一個true是number初始化值的時候,10>0爲true沒有問題。 第二個false將number改變爲-19,輸出false,也沒有問題。 可是當-19改變爲-1的時候,雖然number變了,可是number的改變實際上並無改變plus的值,因此沒有其它地方收到通知,所以也就並無輸出任何值。 直到number從新變爲1時才輸出true。json

實際項目中,computed會被普遍使用到。react-native

(3)action,runInAction

mobx推薦將修改被觀測變量的行爲放在action中。 來看看如下例子:api

import {observable, action} from 'mobx';
class Store {
  @observable number = 0;
  @action add = () => {
    this.number++;
  }
}

const newStore = new Store();
newStore.add();
複製代碼

以上例子使用了ES7的decorator,在實際開發中很是建議用上它,它能夠給你帶來更多的便捷數組

接下來講一個重點action只能影響正在運行的函數,而沒法影響當前函數調用的異步操做 好比官網中給了以下例子

@action createRandomContact() {
  this.pendingRequestCount++;
  superagent
    .get('https://randomuser.me/api/')
    .set('Accept', 'application/json')
    .end(action("createRandomContact-callback", (error, results) => {
      if (error)
        console.error(error);
      else {
        const data = JSON.parse(results.text).results[0];
        const contact = new Contact(this, data.dob, data.name, data.login.username, data.picture);
        contact.addTag('random-user');
        this.contacts.push(contact);
        this.pendingRequestCount--;
      }
  }));
}
複製代碼

重點關注程序的第六行。在end中觸發的回調函數,被action給包裹了,這就很好驗證了上面加粗的那句話,action沒法影響當前函數調用的異步操做,而這個回調毫無疑問是一個異步操做,因此必須再用一個action來包裹住它,這樣程序纔不會報錯。

若是你使用async function來處理業務,那麼咱們可使用runInAction這個API來解決以前的問題。

import {observable, action, useStrict, runInAction} from 'mobx';
useStrict(true);

class Store {
  @observable name = '';
  @action load = async () => {
    const data = await getData();
    runInAction(() => {
      this.name = data.name;
    });
  }
}
複製代碼

4、結合React Native 使用

在React中,咱們通常會把和頁面相關的數據放到state中,在須要改變這些數據的時候,咱們會去用setState這個方法來進行改變。 先設想一個最簡單的場景,頁面上有個數字0和一個按鈕。點擊按鈕我要讓這個數字增長1,就讓咱們要用Mobx來處理這個試試。

import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'
import React, {PureComponent} from 'react'
import {observer} from 'mobx-react'
import {action, observable} from 'mobx'


class MyState {
    @observable num = 1

    @action
    public addNum() {
        this.num++
    }
}

const state = new MyState()

interface Props {

}

interface State {
    
}

/** * 註釋: * 時間: 2019/8/21 0021 11:52 * @author 郭翰林 */
@observer
export default class App extends PureComponent<Props, State> {

    static propTypes = {}

    constructor(props) {
        super(props)
    }

    render() {
        return (
            <View style={{flex: 1, justifyContent: "space-between", alignItems: "center"}}> <View> <Text style={{color: 'red', fontSize: 18, fontWeight: "bold"}}> {state.num} </Text> </View> <TouchableOpacity style={styles.buttonStyle} onPress={() => { state.addNum() }}> <Text style={{color: '#ffffff', fontSize: 14}}> 增長 </Text> </TouchableOpacity> </View>
        )
    }

}

const styles = StyleSheet.create({
    buttonStyle: {
        justifyContent: "center",
        alignItems: "center",
        width: 250,
        borderRadius: 8,
        height: 50,
        marginBottom: 25,
        backgroundColor: '#fc704e'
    }
})
複製代碼

上例中咱們使用了一個MyState類,在這個類中定義了一個被觀測的num變量和一個action函數addNum來改變這個num值。 以後咱們實例化一個對象,叫作newState,以後在個人React組件中,我只須要用@observer修飾一下組件類,即可以愉悅地使用這個newState對象中的值和函數了。

跨組件交互

在不使用其它框架、類庫的狀況下,React要實現跨組件交互這一功能相對有些繁瑣。一般咱們須要在父組件上定義一個state和一個修改該state的函數。而後把state和這個函數分別傳到兩個子組件裏,在邏輯簡單,且子組件不多的時候可能還好,但當業務複雜起來後,這麼寫就很是繁瑣,且難以維護。而用Mobx就能夠很好地解決這個問題。來看看如下的例子:

class MyState {
  @observable num1 = 0;
  @observable num2 = 100;

  @action addNum1 = () => {
    this.num1 ++;
  };
  @action addNum2 = () => {
    this.num2 ++;
  };
  @computed get total() {
    return this.num1 + this.num2;
  }
}

const newState = new MyState();

const AllNum = observer((props) => <div>num1 + num2 = {props.store.total}</div>);

const Main = observer((props) => (
  <div>
    <p>num1 = {props.store.num1}</p>
    <p>num2 = {props.store.num2}</p>
    <div>
      <button onClick={props.store.addNum1}>num1 + 1</button>
      <button onClick={props.store.addNum2}>num2 + 1</button>
    </div>
  </div>
));

@observer
export default class App extends React.Component {

  render() {
    return (
      <div>
        <Main store={newState} />
        <AllNum store={newState} />
      </div>
    );
  }
}
複製代碼

有兩個子組件,Main和AllNum (均採用無狀態函數的方式聲明的組件) 在MyState中存放了這些組件要用到的全部狀態和函數。 以後只要在父組件須要的地方實例化一個MyState對象,須要用到數據的子組件,只須要將這個實例化的對象經過props傳下去就行了。

那若是組件樹比較深怎麼辦呢? 查看最新Mobx5的@inject屬性

關於@observer的一些說明

一般,在和Mobx數據有關聯的時候,你須要給你的React組件加上@observer,你沒必要太擔憂性能上的問題,加上這個@observer不會對性能產生太大的影響,並且@observer還有一個相似於pure render的功能,甚至能起到性能上的一些優化。

所謂pure render見下例:

@observer
export default class App extends React.Component {
  state = {
    a: 0,
  };
  add = () => {
    this.setState({
      a: this.state.a + 1
    });
  };
  render() {
    return (
      <div> {this.state.a} <button onClick={this.add}>+1</button> <PureItem /> </div>
    );
  }
}

@observer
class PureItem extends React.Component {

  render() {
    console.log('PureItem的render觸發了');
    return (
      <div>大家的事情跟我不要緊</div>
    );
  }
}
複製代碼

若是去掉子組件的@observer,按鈕每次點擊,控制檯都會輸出 PureItem的render觸發了 這句話。

React組件中能夠直接添加@observable修飾的變量

@observer
class MyComponent extends React.Component {
  
  state = { a: 0 };

  @observable b = 1;

  render() {
    return(
      <div> {this.state.a} {this.b} </div>
    )
  }
}
複製代碼

在添加@observer後,你的組件會多一個生命週期componentWillReact。當組件內被observable觀測的數據改變後,就會觸發這個生命週期。 注意setState並不會觸發這個生命週期!state中的數據和observable數據並不算是一類。

另外被observable觀測數據的修改是同步的,不像setState那樣是異步,這點給咱們帶了很大便利。

5、總結

Mobx想要入門上手能夠說很是簡單,只須要記住少許概念並能夠完成許多基礎業務了。但深刻學習下去,也仍是要接觸許多概念的。例如Modifier、Transation等等。 最後與Redux作一個簡單的對比

一、Mobx寫法上更偏向於OOP 二、對一份數據直接進行修改操做,不須要始終返回一個新的數據 三、對typescript的支持更好一些 四、相關的中間件不多,邏輯層業務整合是一個問題

相關文章
相關標籤/搜索