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?
- Is my motivation for normalizing states correct in this context?
- What's the best way to normalize the example states?
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