MobX 是一款十分优秀的状态管理库,不但书写简洁还很是高效。固然这是我在使用以后才体会到的,当初试水上车的主要缘由是响应式,考虑到可能会更符合 Vue 过来的思考方式。然而其实二者除了响应式之外并无什么类似之处。javascript
在使用过程当中走了很多弯路,一部分是由于当时扫两眼文档就动手,对 MobX 机制理解得不够;其它缘由是 MobX 终究只是一个库,会受限于 React 机制,以及与其它非 MobX 管理组件的兼容问题。当中不少状况在文档已经给出了说明(这里和这里),我根据本身遇到的再作一番总结。html
与非响应式的组件一块儿工做时,MobX 有时须要为它们提供一份非响应式的数据副本,以避免 observable 被其它组件修改。java
使用 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
数据,只用于生成列表(像骨架同样)。传给 SectionList
的 sections
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
缓存数据能够作一些优化。
好比有一个响应式的数组 arr
,一个组件要根据 arr
是否为空更新。若是直接访问 arr.length
,那么只要数组长度发生变化,这个组件都要 render 一遍。
此时利用 computed 生成,组件只须要判断 isArrEmpty
就能够减小没必要要的更新:
@computed get isArrEmpty () { return this.arr.length <= 0 }
因 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...
【完】