我的Redux状态存储一个comments
对象,该对象存储一系列注释。每个comment
对象中都有一个replies
数组。我将父评论ID和回复ID都传递给我的reducer,目的是从回复数组中删除回复。
我的顶级评论对象的简化版本如下所示:
{
"count": 4,
"data": [
{
id: 123,
text: 'This is a comment',
replies: [
{
id: 479,
text: 'This is a reply',
},
{
id: 293,
text: 'This is another reply',
},
],
},
{
id: 728,
text: 'This is another comment',
replies: [
{
id: 986,
text: 'This is a reply',
},
],
},
],
"pageSize": 5,
cursor: "",
}
这是我的reducer,它似乎将父注释对象包装在一个数组中并展平回复(显然不是所需的结果,但我对解决嵌套数组的最佳方法感到茫然)。
case types.DELETE_REPLY_SUCCESS: {
const content = Object.assign({}, state);
content.data = content.data.map((comment) => {
const newObj = { ...comment };
if (newObj.id === action.parentCommentId) {
return newObj.replies.filter(reply => reply.id !== action.replyId);
}
return newObj;
});
return content;
}
答案 0 :(得分:3)
上面的答案更直接但我想分享一些我的想法。
首先,Redux中规范化状态的this链接解决了我的一个大问题......处理嵌套数据结构......你的代码变得非常复杂。 90%的减速机应该非常简单。 使用嵌套数据结构,它们变得复杂。
尽管normalizer是一个非常标准化的状态标准工具,但我建议您首先编写自己的标准。通过这种方式我学到了很多东西。
什么是规范化数据结构?
这是平的。它通常是关系型的。
Dan Abramov(建立Redux)建议存储分组的数据。所以你的评论成为一个组,你的回复分享另一个。就像在关系数据库中一样。每个“组”都有自己的“表”。
起初这对我来说似乎有点直观,因为感觉你在编写更多的数据结构。你不是......而且收益也很值得。
因此,您可以存储类似这样的数据
{
"comments": {
"allIds" : [1,2,3,4],
"byId": {
1: {
replies: [12],
comment: "a comment"
},
2: {
replies: [13],
comment: "another comment"
},
3: {
replies: [14],
comment: "one more comment"
},
4: {
replies: [15],
comment: "oh look, a comment"
},
}
},
"replies" : {
"allIds" : [12,13,14,15],
"byId": {
12: {
comments: [1],
reply: "a reply"
},
13: {
comments: [2],
reply: "another reply"
},
14: {
comments: [3],
reply: "yet another reply"
},
15: {
comments: [4],
reply: "a reply"
}
}
}
为什么这么重要?
这会以什么方式让生活更轻松?首先,如果我们想要显示评论列表,我们只需map()
通过我们的allIds
数组,并使用第一个参数来访问密钥以获取数据。
这意味着我们可以遍历单个数组而不是通过嵌套对象。
以同样的方式,并回答您的问题,您可以使用filter()
代替map()
删除元素。我不打算在这里解释过滤器。需要十秒钟才能找到比我能解释的更好的例子。
然后确保您遵循标准的Redux方式。
使用reducer复制数据结构。你的州应该在没有数据的情况下初始化......这样你就知道你做得对了。
编写处理它的特定状态的减速器(注释减速器等)对我来说,这些通常由返回状态或返回新状态的switch语句组成。
编写为减速器提供所需新数据的操作。 始终遵循使用函数处理一项工作的JS原则。操作不应该做多件事。
注意但是使用像thunk这样的中间件,操作可以调度其他可能导致逻辑非常棒的操作!
评论的删除操作可能看起来像
那样简单function deleteComment(commentId) {
return {
type: "delete project",
payload: commentId
}
}
这给了我们所需要的一切。
通过在我们的reducer中使用前面提到的switch语句,我们可以检查查看正在调度的type
操作。 (这告诉我们如何处理提供的数据)以及要删除的元素,即有效负载。
您可以按照这种简单的方法来满足85%的应用需求。
我知道Redux还有很多东西,但是这种方法确实帮助我了解了如何查看Redux以及如何管理和操作状态。 我强烈建议完成上面链接的Dan Abramov的整个eggheads.io教程。他在解释几乎所有细节方面做得非常出色。
我希望对你的简短问题这么长的答案有所帮助。
答案 1 :(得分:2)
在redux中更新嵌套结构可能非常讨厌。我建议使用更扁平的结构。您可以使用normalizer或手动执行此操作。
但是,如果您需要更新现有结构,请执行以下步骤:
示例强>
const state = {"count":4,"data":[{"id":123,"text":"This is a comment","replies":[{"id":479,"text":"This is a reply"},{"id":293,"text":"This is another reply"}]},{"id":728,"text":"This is another comment","replies":[{"id":986,"text":"This is a reply"}]}],"pageSize":5,"cursor":""};
const action = {
parentCommentId: 123,
replyId: 479
};
const deleteReplySuccess = () => {
const data = state.data;
// find the index of the comment you want to update
const commentToChangeIndex = data.findIndex(({ id }) => id === action.parentCommentId);
// if it doesn't exist return the state
if(commentToChangeIndex === -1) {
return state;
}
// get the comment
const comment = data[commentToChangeIndex];
// create an updated comment with filtered replies array
const updatedComment = {
... comment,
replies: comment.replies.filter(reply => reply.id !== action.replyId)
};
// create a new state using object spread or assign
return {
...state,
data: [ // create a new data array
...data.slice(0, commentToChangeIndex), // the comments before the updated comment
updatedComment, // the updated comment
...data.slice(commentToChangeIndex + 1) // the comments after the updated comment
]
};
};
console.log(deleteReplySuccess());