# mobx-react原理解析
###### tags: `mobX`
## :memo: Redux的好處 ?
1. 通過action、reducer來完成應用的數據流管理、邏輯簡單
2. reducer函數的設計,讓我們代碼變得可觀測、可回溯
3. action的設計,特別適用於多數據連動的場景
## :rocket: 使用 mobx 时,借鉴了 redux 架構的優點:
* 單一數據源,這樣避免了子組件、父組件狀態同步的問題
* 可以做到讓組件無狀態化
* 使用Provider注入,讓store、actions可以在子組件中,通過props訪問使用
~~下面是一些不同點:~~
* **mobx** 使用的是 `@inject` 裝飾器語法注入,redux使用的是 `connect` 語法注入
* **mobx** 使用 `@observer` 語法,讓一個component能響應 `store` 字段更新
* **mobx** 會動態精確綁定數據字段和對應component關係,`redux` 使用 `connect` 參數手動控制傳遞哪些字段
* **mobx** 直接修改store的狀態,但是必須在`@action` 修飾的函數中完成,`@action` 的語意,表示這是一個修改狀態的操作
* **mobx** redux Provider傳遞 `store`是強約定,mobx Provider靈活傳遞 `store` `actions`,也可以是其他名字,例如: `db`
* **redux** 使用了比較難以理解的高階函數和參數 `connect` `combineReducers` `bindActionCreators` `mapDispatchToProps`,mobX方案,除了使用decorator語法,沒有其它讓人感覺理解酷男的函數。
* **redux**引入了數據流,mobx沒有數據流的概念,通過 actions 直接改變數據。
### 定義一個可觀測的store
```javascript=16
import { observable,action } from 'mobx';
class AppStore {
@observable number = 0;
@action changeNumber(){
this.number++;
}
}
export default AppStore;
// state:狀態
// setState:狀態更新時Render component
// @observable:需要被監聽的狀態
// @obser:狀態更新時Render component
```
### 觀測store,mobX中提供了observer方法,確保數據更新會觸發重新渲染
> observer可以收集組建依賴的數據,一旦收到數據變化的通知就會將組件重新渲染,從而做到更細粒度的更新,這是redux和react很難做到的,因為react中組建重新渲染基本是依賴於setState和接收到新的props,子組件的渲染一定是伴隨著父組建的渲染,而mobX可以做到只渲染依賴數據變化的那個組件。
```javascript=16
import React, { Component } from 'react';
import { observer } from 'mobx-react';
@observer
class App extends Component {
render() {
const{store} = this.props;
return <div onClick={store.changeNumber.bind(store)}>
{store.number}
</div>
}
};
export default App;
```
>autorun基本上就是監聽,`observer`適用 `mobx.autorun` 包裝了組件的render函數以確保任何組建渲染中使用的數據變化時都可以強制刷新組件。
### 最後我們打store注入進去,然後組建渲染出來
```javascript=16
import React from 'react';
import { render } from 'react-dom';
const store = new AppStore();
render(
<App store={store} />
document.getElementById('root')
);
```
> mobx是用typescript寫的,它能和vue一樣數據監聽,底層Object.defineProperty或Proxy來劫持數據,可以做到更細粒度的選染。

- 觀察者模式
| 名稱 | 事件 |
| --------- |:-------------------------------------------------
| Actions | action會由事件所觸發,而action也是唯一可修改state |
| State | 可觀察(observable)並最小化定義 |
| Computed | pure function可拿state直接計算出值 |
| Autorun | 可以自動追蹤所引用的可觀察數據,並在數據發生變化時自動觸發|
| Reactions | state變化會觸發反應像更新UI |
## :rocket: mobX vs Redux:
| Redux | MobX |
| --------- |:-------------------------------------------------
| 分成Container,Component|全部Component皆使用@observer訂閱狀態|
| 需要Middleware(redux-thunk,redux-saga)|沒有Middleware|
| 狀態更新必須是immutable|沒有特定要求|
| Provider負責將Store注入react應用|Provider使用mobx-react提供的Provider將所有stores注入應用|
| connect負責將store state注入容器組件,並選擇特定狀態作為容器組件props傳遞|使用inject將特定store注入某組件,store可以傳遞狀態或action;然後使用observer保證組件能響應store中的可觀察對象(observable)變更,即store更新,組件視圖響應式更新|
## :rocket: 狀態管理
>狀態管理庫,無論是Redux還是MobX,其本質都是為了解決狀態管理混亂,無法有效同步的問題,他們都支持:
1. 統一維護管理應用狀態:
2. 某一狀態只有一個可信數據來源(通常命名為store,只狀態容器)
3. 操作更新狀態方式統一,並且可控(通常以action方式提供更新狀態的途徑)
4. 支持將store與React組件連接,如`React-redux` `MobX-react`,通常使用狀態管理庫後,我們將React組件從業務上畫分為兩類:
>1.容器組件(Container Components):負責處理具體業務和狀態數據,將業務或狀態處理傳入展示型組件
2.展示行組件(Presentation Components):負責展示視圖,視圖交互回調內調用傳入的處理函數
## :rocket: classComponent與functionComponent差別?
* classComponent
```javascript=16
import React, { Component } from 'react';
import { View, Text, TextInput } from 'react-native';
import { inject, observer } from 'mobx-react'
import { observable, computed } from 'mobx'
@inject('store')
@observer
class Counter extends Component {
@observable fireMessage = "HaveYouSeenABushfire"
@observable temp = 30
@computed get ferh() {
return (this.temp * 9 / 5) + 32
}
render() {
return (
<View>
<Text>{this.props.store.title}</Text>
<Text>{this.ferh}</Text>
<TextInput onChangeText={this.props.store.onChangeText} />
<Test fireMessage={this.fireMessage}></Test>
</View>
)
}
}
const Test = observer(({ fireMessage }) =>
<Text>{fireMessage}
</Text>)
export default Counter
```
* functionComponent
```javascript=16
import React, { useState } from 'react'
import { View, Text, TextInput } from 'react-native';
import { inject, observer } from 'mobx-react'
import { observable } from 'mobx'
inject('store')
const CounterHook = observer(() => {
const [CounterData] = useState(() =>
observable({
fireMessage: "HaveYouSeenABushfire",
temp: 30,
get ferh() {
return (this.temp * 9 / 5) + 32
}
})
)
return (
<View>
<Text>{store.title}</Text>
<Text>{CounterData.ferh}</Text>
<TextInput onChangeText={store.onChangeText} />
<Test fireMessage={this.fireMessage}></Test>
</View>
)
})
export default CounterHook
```