React Native 搭配 MobX 使用心得

MobX 是一款十分優秀的狀態管理庫,不但書寫簡潔還很是高效。固然這是我在使用以後才體會到的,當初試水上車的主要緣由是響應式,考慮到可能會更符合 Vue 過來的思考方式。然而其實二者除了響應式之外並無什麼類似之處javascript

在使用過程當中走了很多彎路,一部分是由於當時掃兩眼文檔就動手,對 MobX 機制理解得不夠;其它緣由是 MobX 終究只是一個庫,會受限於 React 機制,以及與其它非 MobX 管理組件的兼容問題。當中不少狀況在文檔已經給出了說明(這裏這裏),我根據本身遇到的再作一番總結。html

與非響應式組件兼容問題

與非響應式的組件一塊兒工做時,MobX 有時須要爲它們提供一份非響應式的數據副本,以避免 observable 被其它組件修改。java

observable.ref

使用 React Navigation 導航時,若是要交由 MobX 管理,則須要手動配置導航狀態棧,此時用 @observable.ref 「淺觀察」可避免狀態被 React Navigation 修改時觸發 MobX 警告。react

當 Navigator 接受 navigation props 時表明導航狀態爲手動管理。react-native

import { addNavigationHelpers, StackNavigator } from 'react-navigation'
import { observable, action } from 'mobx'
import { Provider, observer } from 'mobx-react'
import AppComp from './AppComp'

const AppNavigator = StackNavigator({
  App: { screen: AppComp },
  // ...
}, {
  initialRouteName: 'App',
  headerMode: 'none'
})

@observer
export default class AppNavigation extends Component {
  @observable.ref navigationState = {
    index: 0,
    routes: [
      { key: 'App', routeName: 'App' }
    ],
  }

  @action.bound dispatchNavigation = (action, stackNavState = true) => {
    const previousNavState = stackNavState ? this.navigationState : null
    this.navigationState = this.AppNavigator.router.getStateForAction(action, previousNavState)
    return this.navigationState
  }

  render () {
    return (
      <Provider
        dispatchNavigation={this.dispatchNavigation}
        navigationState={this.navigationState}
      >
        <AppNavigator navigation={addNavigationHelpers({
          dispatch: this.dispatchNavigation,
          state: this.navigationState,
        })} />
      </Provider>
    )
  }
}

observable.shallowArray()observable.shallowMap()

MobX 還提供其它方便的數據結構來存放非響應式數據。數組

好比使用 SectionList 的時候,咱們要爲其提供數據用於生成列表,因爲 Native 官方的實現跟 MobX 不兼容,這個數據不能是響應式的,否則 MobX 會報一堆警告。緩存

MobX 有個 mobx.toJS() 方法能夠導出非響應式副本;若是結構不相同還可使用 @computed 自動生成符合的數據。但這兩個方法每次添加項目都要所有遍歷一遍,可能會存在性能問題。數據結構

這時其實能夠維護一個 observable.shallowArray,裏面只放 key 數據,只用於生成列表(像骨架同樣)。傳給 SectionListsections props 時 slice 數組複製副本(shallowArray 裏的數據非響應式,因此只需淺複製,複雜度遠小於上面兩種方式)。app

而後 store 維護一個 observable.map 來存放每一個項的數據,在項(item)組件中 inject store 進去,再利用 key 從 map 中獲取數據來填充。ide

經過 shallowArray 可讓 MobX 識別列表長度變化自動更新列表,利用 map 維護項數據可使每一個項保持響應式卻互不影響,對長列表優化效果很明顯。

// store comp
class MyStore {
  @observable sections = observable.shallowArray()
  @observable itemData = observable.map()

  @action.bound appendSection (section) {
    const data = []
    section.items.forEach(action(item => {
      this.itemData.set(item.id, item)
      data.push({key: item.id})
    }))
    this.sections.push({
      key: section.id,
      data
    })
  }
}
// MyList comp
import { SectionList } from 'react-native'
@inject('myStore')
@observer
class MyList extends React.Component {
  _renderItem = ({item}) => <SectionItem id={item.key} />

  render () {
    return (
      <SectionList
        getItemLayout={this._getItemLayout}
        sections={this.props.myStore.sections.slice()}
        renderSectionHeader={this._renderSectionHeader}
        renderItem={this._renderItem}
      />
    )
  }
}
// SectionItem comp
@inject('myStore')
@observer
class SectionItem extends React.Component {
  render () {
    const {myStore, id} = this.props
    const itemData = myStore.itemData.get(id)
    return (
      <Text>{itemData.title}</Text>
    )
  }
}

computed

利用 @computed 緩存數據能夠作一些優化。

好比有一個響應式的數組 arr,一個組件要根據 arr 是否爲空更新。若是直接訪問 arr.length,那麼只要數組長度發生變化,這個組件都要 render 一遍。

此時利用 computed 生成,組件只須要判斷 isArrEmpty 就能夠減小沒必要要的更新:

@computed get isArrEmpty () {
  return this.arr.length <= 0
}

observable.map

因 JS 機制 MobX 不能檢測屬性的增刪,因此最好用 observable.map 取代簡單 {} 對象。另外 MobX 沒有提供 Set 支持,能夠用 key 和 value 同樣的 Map 代替。

避免在父組件中訪問子組件的屬性

這條規則在文檔也提到,緣由很簡單,MobX 對於一個 observer 組件,是經過訪問屬性來記錄依賴的。因此哪怕父組件裏沒有用到這個屬性,只是爲了做爲 props 傳給子組件,MobX 仍是算它依賴了這個屬性,因而會產生沒必要要的更新。最好的方式是將數據統一放在 store 中,子組件經過 inject store 方式獲取數據。

小組件

因爲 React 的機制,MobX 只能在組件層面發光發熱,對於組件內部就無能爲力了。因此大組件用 MobX 很容易卡死(用其它也會),小組件才能真正發揮 MobX 自動管理更新的優點。

博客連接:https://blog.crimx.com/2017/1...

【完】

相關文章
相關標籤/搜索