数据状态管理

我们会把App数据统一管理起来,WeTrident中通过redux来做数据状态的管理。并且设计了一套固定的数据状态隔离和共享的方案来解决redux store上数据管理混乱的问题。

数据的隔离和共享

Redux提供来足够的数据共享能力,但是所有数据在一个store上,既提供了方便,又带来了危害,redux的store本质上仍然是一个全局变量,虽然通过固定的数据流来维护数据,但是始终无法完全摆脱全局变量多处共同维护和使用一份数据导致的问题。想象一下,如果整个App有上百个页面,所有共享数据均无规则存储在store上,那将会是一番如何混乱的景象。

WeTrident对store上的数据结构和层次做了明确的区分,以求在隔离和共享之间找到最佳的折中点。与我们的目录结构类似,store上的数据结构也分为了三层:globalmodulePrivatescene

global为全局共享数据,这些数据可以供所有模块的页面读取,维护global数据对应的action也可绑定到Scene以支持页面内刷新全局数据。

modulePrivate为模块内私有数据,这些数据仅仅可以在同一个模块内的不同页面间共享,modulePrivate上数据对应的action也可绑定到Scene以支持页面内刷新模块内共享数据。

Scene级别的数据为页面私有数据,其他页面无法共享,如果需要传递可以使用传参跳转的方式实现。

通过上述层次划分,使用过程中,按 scene -> modulePrivate -> global的顺序去存放数据,尽可能让数据的访问范围变小。

存放数据到Scene数据区

定义更新数据需要的actions到Scene作用域。

  1. // modules/book/BookListScene/index.js
  2. /**
  3. * 定义scene级别的actions
  4. */
  5. actions: {
  6. updateBookList: v => v
  7. },
  8. /**
  9. * 定义scene级别的reducer
  10. */
  11. reducers: {
  12. updateBookList: (state, action) => ({
  13. ...state,
  14. bookList: action.payload
  15. })
  16. },

使用 action 更新数据到store。

  1. // modules/book/BookListScene/BookListScene.js
  2. componentDidMount () {
  3. this.props.updateBookList([
  4. {
  5. 'title': '经济学原理',
  6. 'author': '曼昆',
  7. 'coverURL': 'https://img3.doubanio.com/view/subject/l/public/s3802186.jpg',
  8. 'publishTime': '2009-4-1',
  9. 'pages': 540,
  10. 'ISBN': '9787301150894'
  11. },
  12. {
  13. 'title': '失控-全人类的最终命运和结局',
  14. 'author': '[美] 凯文·凯利 ',
  15. 'coverURL': 'https://img3.doubanio.com/view/subject/l/public/s4554820.jpg',
  16. 'publishTime': '2010-12',
  17. 'pages': 707,
  18. 'ISBN': '9787513300711'
  19. }
  20. ])
  21. }

调用完成可以看到日志如下,bookList更新到了新的next state上。数据状态管理 - 图1bookList会默认挂到BookListScenethis.props 上。

上面截图中我们可以看到整个App的state store的结构:数据状态管理 - 图2图中可以看的出,WeTrident App的模块数据独立于其他模块,页面数据也独立其他页面的数据。

接着我们使用这些数据来填充界面,可以直接通过 this.props 使用bookList

  1. // modules/book/BookListScene/BookListScene.js
  2. render () {
  3. return (
  4. <View>
  5. {(this.props.bookList || []).forEach(bookInfo => {
  6. return <BookListItem bookInfo={bookInfo} />
  7. })}
  8. </View>
  9. )
  10. }

上面这种方式需要开发者对自己写很多特定的action/reducer,trident的scene默认提供了setSceneState函数,可以快速的在当前Scene的数据区存放数据,例如上面更新数据的例子可以写为:

  1. // modules/book/BookListScene/BookListScene.js
  2. componentDidMount () {
  3. this.props.setSceneState({
  4. bookList: [
  5. {
  6. 'title': '经济学原理',
  7. 'author': '曼昆',
  8. 'coverURL': 'https://img3.doubanio.com/view/subject/l/public/s3802186.jpg',
  9. 'publishTime': '2009-4-1',
  10. 'pages': 540,
  11. 'ISBN': '9787301150894'
  12. },
  13. {
  14. 'title': '失控-全人类的最终命运和结局',
  15. 'author': '[美] 凯文·凯利 ',
  16. 'coverURL': 'https://img3.doubanio.com/view/subject/l/public/s4554820.jpg',
  17. 'publishTime': '2010-12',
  18. 'pages': 707,
  19. 'ISBN': '9787513300711'
  20. }
  21. ]
  22. })
  23. }

存放数据到 modulePrivate 区

modulePrivate的数据模块内私有,模块内的Scene可以共享这些数据。在我WeBookStore中,考虑BookDetailScene中也会用到书籍列表数据,bookList 放到 BookListScene下是不够的,需要提升到book 模块的共享数据区。要完成这个提升首先我们需要在module中增加一套action/reducer,然后将对应的数据绑定到使用数据的Scene即可。

  1. // modules/book/index.js
  2. export default {
  3. moduleName: 'book',
  4. initialState: {
  5. // moduleCount: 0
  6. },
  7. sceneList: require('./manifest').default,
  8. actions: {
  9. updateBookList: v => v
  10. },
  11. reducers: {
  12. updateBookList: (state, action) => ({ ...state, bookList: action.payload })
  13. }
  14. }
  1. // modules/book/BookListScene/index.js
  2. export default (global, ModulePrivate) => ({
  3. // ...
  4. /**
  5. * 将module级别的共享数据映射到props
  6. */
  7. mapModuleState: state => ({
  8. bookList: state.bookList,
  9. }),
  10. /**
  11. * 将module级别的actions映射到props
  12. */
  13. moduleActions: {
  14. updateBookList: ModulePrivate.actions.updateBookList
  15. },
  16. // ...
  17. })

修改 BookListScene 使用 updateBookList 更新模块数据

  1. // modules/book/BookListScene/BookListScene.js
  2. async componentDidMount () {
  3. this.props.updateBookList(await BookListService.requestAllBookList())
  4. }

修改完成以后可以看到数据已经存放到 book 模块的modulePrivate上, 改完以后render内不用修改。数据状态管理 - 图3

同样的在BookDetailScene中可以很容易的共享 modulePrivate 上的bookList。

  1. // modules/book/BookDetailScene/index.js
  2. export default (global, ModulePrivate) => ({
  3. // ...
  4. /**
  5. * 将module级别的共享数据映射到props
  6. */
  7. mapModuleState: state => ({
  8. bookList: state.bookList,
  9. }),
  10. /**
  11. * 将module级别的actions映射到props
  12. */
  13. moduleActions: {
  14. updateBookList: ModulePrivate.actions.updateBookList
  15. },
  16. // ...
  17. })