# 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來劫持數據,可以做到更細粒度的選染。 ![](https://i.imgur.com/CmniUhz.png) - 觀察者模式 | 名稱 | 事件 | | --------- |:------------------------------------------------- | 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 ```