Redux: Normalizing Global State

て烟熏妆下的殇ゞ 提交于 2020-01-05 05:37:07

问题


Assume the following situation:

1. Page has many posts
2. Post has many comments

I have the following reducers:

1. PagesReducer
2. PostsReducer
3. PostReducer
4. CommentsReducer

I have the following state right now:

pagesByTitle: {
  dorrisPage: {
    isFetching: false,
    data: {
      _id: "..."
      title: "dorrisPage",
    },
    posts: [
      {
        isFetching: false,
        data: {
          _id: "..",
          body: ".."
        },
        comments: [..]
       }
    ]
  }
}

The above structure looked okay initially, but I realized that I had to pass down the action for child states. For example, if I dispatched an action called

ADD_COMMENT

I would pass the action down to PagesReducer, PostsReducer, PostReducer, and CommentsReducer, and finally the CommentsReducer will handle that action. I think this is when I realized why normalizing states is recommended in Redux.

Can you help me with the following questions?

  1. Is my motivation for normalizing states correct in this context?
  2. What's the best way to normalize the example states?

回答1:


you should avoid nesting.

quote from redux docs:

In a more complex app, you’re going to want different entities to reference each other. We suggest that you keep your state as normalized as possible, without any nesting. Keep every entity in an object stored with an ID as a key, and use IDs to reference it from other entities, or lists. Think of the app’s state as a database. This approach is described in normalizr's documentation in detail.

For normalize state you can use normalizr

pages:{
    items:{
        1:{id: 1,title: "dorrisPage", posts:[33,57]}
        2:{id: 2, title: "anotherPage",posts:[57]}
    },
    isFetching: false,
    itemIds:[1,2,..]
},
posts:{
    items:{
        33:{id: 33,title: "Post33", comments:[1,2]}
        57:{id: 57, title: "Post57", comments:[]}
    },
    isFetching: false,
    itemIds:[33,57,..]
}
comments:{
    items:{
        1:{id: 1, user: "user1", text:"fds"}
        2:{id: 2, user: "user2", text:"fds2"}
    },
    isFetching: false,
    itemIds:[1,2,..]
}

"itemIds" is neccessary for items ordering

then reducers may look like this

export const posts = (state = initialState, action) => {
    switch (action.type) {

        case type.FETCH_POSTS_REQUEST:
        case type.FETCH_POSTS_FAILURE:
            return {...state, isFetching: action.isFetching};
        case type.FETCH_POSTS_SUCCESS:
        return {...state,
            isFetching: action.isFetching,
            items: action.entities.posts, itemsIds: action.result
        };

        case type.DELETE_POST:
        return {...state,
            items: omit(state.items, action.id),
            itemsIds: state.itemsIds.filter(id=>id !== action.id)
        };
        case type.UPDATE_POST:
        return {...state, items: {...state.items,
                [action.post.id]: {...state.items[action.post.id],...action.post}}};

        default:
        return state;
    }
}

much easier to query post by id:

const mapStateToProps = (state,ownProps) =({
    post:state.posts.items[ownProps.id]
})

for computing derived data from redux store, you can use Reselect for creating memoized, composable selector functions

video tutorial



来源:https://stackoverflow.com/questions/38032963/redux-normalizing-global-state

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!