Reputation: 13
I'm updating a "message board" project I did (Brad Traversy's MERN stack course) to include comment likes. For a simple example, think Facebook: you create a post which people can like, and people can respond with comments, which can also be liked.
I have a "post" piece of state, which contains post likes (post.likes array), comments (post.comments array), and comment likes (where things get tricky: an array nested inside the post.comments array).
How do I update the nested arrays in my reducer so my page re-renders properly? Right now, it records the action and shows the new likes/dislikes when the page reloads manually, but it won't reload the page itself.
I've tried updating the state, but the reality is, I'm not entirely sure how to loop through and update something deeply nested.
Here is my actual post state, courtesy of Redux DevTools.
post: {
posts: [],
post: {
_id: '5cebd8bcdc17fd5cd7e57a45',
text: 'This is my brand new post.',
name: 'Bobby Smith',
avatar: '//www.gravatar.com/avatar/414868368393e3ba8ae5ff93eeb98de6?s=200&r=pg&d=mm',
user: '5cd646c9a632b51373121995',
likes: [
{
_id: '5cebd8d1dc17fd5cd7e57a47',
user: '5cd36ce5fda120050ee64160'
}
],
comments: [
{
date: '2019-05-27T12:32:16.172Z',
likes: [ /*-------- This ---------*/
{
_id: '5cebd8e1dc17fd5cd7e57a48',
user: '5cd646c9a632b51373121995'
}
],
_id: '5cebd8d0dc17fd5cd7e57a46',
text: 'And this is my brand new response.',
name: 'John Doe',
avatar: '//www.gravatar.com/avatar/b2b146dba9e0023cb56637f0df4aa005?s=200&r=pg&d=mm',
user: '5cd36ce5fda120050ee64160'
}
],
date: '2019-05-27T12:31:56.598Z',
__v: 3
},
loading: false,
error: {}
}
}
Reducer:
const initialState = {
posts: [],
post: null,
loading: true,
error: {}
}
export default function(state = initialState, action) {
const { type, payload } = action
switch (type) {
case UPDATE_COMMENT_LIKES:
return {
...state,
post: { ...state.post, comments: ???? }
}
default:
return state
}
}
It's passing in the post ID and the user ID, and then filtering based on whether or not they already exist. I'll also add the action creators, just for clarity.
// Add like to comment
export const addCommentLike = id => async dispatch => {
try {
const res = await axios.put(`/api/posts/comment/like/${id}`)
dispatch({
type: UPDATE_COMMENT_LIKES,
payload: { id, likes: res.data }
})
} catch (err) {
dispatch({
type: POST_ERROR,
payload: { msg: err.response.statusText, status: err.response.status }
})
}
}
// Remove like from comment
export const removeCommentLike = id => async dispatch => {
try {
const res = await axios.put(`/api/posts/comment/unlike/${id}`)
dispatch({
type: UPDATE_COMMENT_LIKES,
payload: { id, likes: res.data }
})
} catch (err) {
dispatch({
type: POST_ERROR,
payload: { msg: err.response.statusText, status: err.response.status },
loading: false
})
}
}
Right now, it's updating everything in the database, but it's not updating the state immediately and triggering a re-render.
I'd appreciate any help I can get.
Thanks!
Upvotes: 0
Views: 560
Reputation: 11
I know it's a bit too late but it might help for someone. I also did this course btw ;) So in your case reducer would look like this:
case UPDATE_COMMENT_LIKES:
return {
...state,
post: {
...state.post,
comments: state.post.comments.map((comment) =>
comment._id === payload.id
? { ...comment, likes: payload.likes }
: comment
)
},
loading: false
};
Upvotes: 1
Reputation: 1568
It would be better if you'll split the remove/add logic in your reducer as well:
ADD_COMMENT_LIKES
- will add the like to the nested likes arrayREMOVE_COMMENT_LIKES
- will filter the chosen like by its idexport default function(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case ADD_COMMENT_LIKES:
return {
...state,
post: {
...state.post,
comments: [
...state.post.comments,
likes: [
...state.post.comments.likes,
payload <--- inserting the new like
]
]
}
}
case REMOVE_COMMENT_LIKES:
return {
...state,
post: {
...state.post,
comments: [
...state.post.comments,
likes: [
...state.post.comments.likes.filter((item) => item.id !== payload.id)
]
]
}
default:
return state
}
}
Read how to combine reducers, and form your state as a single independent branches. It will help you structure your reducer in a more maintainable structure.
Upvotes: 0