如何正确处理双嵌套数组的状态(ReactJS + Redux)
How to properly handle state in double nested arrays (ReactJS + Redux)?
我试图建立以下内容:一个论坛,在那里可以创建一个帖子,触发ADD_POST,并创建该帖子,并添加到'posts'对象数组。每个"post"对象初始化为一个"comments"数组,该数组将保存在该帖子中输入的评论文本("commentTxt")。
let postReducer = function(posts = [], action) {
switch (action.type) {
case 'ADD_POST':
return [{
id: getId(posts), //just calls a function that provides an id that increments by 1 starting from 0
comments: [
{
id: getId(posts),
commentTxt: ''
}
]
}, ...posts]
然后,当用户输入该帖子时,有一个评论部分,用户可以在其中输入评论文本,并将一个新对象(通过'ADD_COMMENT')添加到'posts.comments'数组
case 'ADD_COMMENT':
return posts.map(function(post){
//find the right 'post' object in the 'posts' array to update the correct 'comments' array.
if(post.id === action.id){
//update 'comments' object array of a 'post' object by adding a new object that contains 'commentTxt', and replaces the current 'comments' array
return post.comments = [{
id: action.id,
//new object is made with text entered (action.commentTxt) and added to 'post.comments' array
commentTxt: action.commentTxt
}, ...post.comments]
}
})
并显示它。每次添加新的注释时,都会在数组中呈现一个新的注释对象和先前的注释对象。想要做如下的事情:
{
this.props.post.comments.map((comment) => {
return <Comment key={comment.id} comment={comment} actions={this.props.actions}/>
})
}
我听说直接改变状态是不推荐的,所以我将感谢任何指导或见解如何正确地这样做。
您可以考虑规范化您的数据。因此,与其像这样存储结构体:
posts: [{
title: 'Some post',
comments: [{
text: 'Hey there'
}]
}]
你可以这样存储它们:
posts: [{
id: 1,
title: 'Some post'
}]
comments: [{
id: 4,
postId: 1,
text: 'Hey there'
}]
一开始比较麻烦,但是有很大的灵活性。
或者,您可以修改您的ADD_COMMENT reducer:
return posts.map(function(post) {
if (post.id !== action.id) {
return post
}
return {
...post,
comments: [
...post.comments,
{
id: action.id,
commentTxt: action.commentTxt
}
]
}
}
注意:在最后一个解决方案中,没有突变。我不知道如果有大量的注释,它会如何运行,但除非你有很好的理由,否则我不会对这种情况进行预优化。
正如Christopher Davies在他的回答中所说,您应该使您的状态正常化。假设我们有一个这样的形状:
const exampleState = {
posts: {
'123': {
id: '123',
title: 'My first post',
comments: [] // an array of comments ids
},
'456': {
id: '456',
title: 'My second post',
comments: [] // an array of comments ids
}
},
comments: {
'abc': {
id: 'abc',
text: 'Lorem ipsum'
},
'def': {
id: 'def',
text: 'Dolor sit'
},
'ghi': {
id: 'ghi',
text: 'Amet conseguir'
}
}
}
好的,现在让我们编写一些动作创建器来创建可以改变状态的动作:
const addPost = (post) => ({
type: 'ADD_POST',
post
})
const addComment = (postId, comment) => ({ // for the sake of example, let's say the "comment" object here is a comment object returned by some ajax request and having it's own id
type: 'ADD_COMMENT',
postId,
comment
})
然后,您将需要两个reducer来处理posts片和comments片:
const postsReducer = (posts = {}, action = {}) => {
switch(action.type) {
case 'ADD_POST':
const id = getId(posts)
return {
...posts,
[id]: action.post
}
case 'ADD_COMMENT':
return {
...posts.map(p => {
if (p.id == action.postId) {
return {
...p,
comments: p.comments.concat([action.comment.id])
}
}
return p
})
}
default:
return state
}
}
const commentsReducer = (comments = {}, action = {}) => {
switch(action.type) {
case 'ADD_COMMENT':
return {
...comments,
[action.comment.id]: action.comment
}
default:
return state
}
}
让我们还创建一些选择器来从状态中获取数据:
const getPost = (state, id) => state.posts[id]
const getCommentsForPost = (state, id) => ({
const commentsIds = state.posts[id].comments
return state.comments.filter(c => commentsIds.includes(c.id))
})
然后,你的组件:
const PostLists = (posts) => (
<ul>
{posts.map(p) => <Post key={p} id={p} />}
</ul>
)
PostLists.propTypes = {
posts: React.PropTypes.arrayOf(React.PropTypes.string) //just an id of posts
}
const Post = ({id, title, comments}) => (
<li>
{title}
{comments.map(c) => <Comment key={c.id} {...c}/>}
</li>
)
Post.propTypes = {
id: React.PropTypes.string,
comments: React.PropTypes.arrayOf(React.PropTypes.shape({
id: React.PropTypes.string,
text: React.PropTypes.text
}))
}
const Comment = ({ id, text }) => (
<p>{text}</p>
)
现在,连接的容器:
// the mapStateToProps if very simple here, we just extract posts ids from state
const ConnectedPostLists = connect(
(state) => ({
posts: Objects.keys(state.posts)
})
)(PostLists)
// The ConnectedPost could be written naively using the component props passed as the second argument of mapStateToProps :
const ConnectedPost = connect(
(state, { id }) => ({
id,
title: getPost(state, id).title,
comments: getCommentsForPost(state, id)
})
)(Post)
这是要工作的,但是,如果你有很多帖子,你会遇到ConnectedPost
组件的性能问题,因为mapStateToProps依赖于组件自己的道具将触发连接组件的重新渲染在状态的任何变化
所以我们应该这样重写:
// Since the post id is never intended to change for this particular post, we can write the ConnectedPost like this :
const ConnectedPost = connect(
(_, { id}) => (state) => ({
id,
title: getPost(state, id).title,
comments: getCommentsForPost(state, id)
})
)
好啦!我没有测试这个例子,但我认为它可以帮助你看到你需要去的方向
- reactjs-redux,是否可以有一个由2个组件使用的状态
- 使用ReactJS+Redux时,我需要componentWillReceiveProps和replaceProps吗
- reactJs redux:如何对用户事件调度操作并将操作与化简器和存储链接
- Reactjs,redux授权注销问题
- 使用ReactJS使用Redux的简单状态更新事件
- ReactJS,Redux:如何返回STATE中的一组项
- 如何使用 ReactJS、Redux 和 ES2015 将 onChange 回调正确绑定到“this”
- ReactJS + Redux:如何等到调度完成后再进入下一步
- 如何在ReactJS + Redux中存储Node.js Express与MongoDB的会话
- reactjs/redux -我这样做对吗?很少有关于代码的问题
- Reactjs redux在页面刷新时存储状态.componentWillUnmount不工作
- 如何正确地从ReactJS+Redux应用程序进行REST调用
- ReactJS+Redux:如何使用Material UI's将按钮提升为<input/>
- ReactJS+Redux:为什么不;尽管安装了热装载机,但不会对其工作作出反应
- 如何在不必连接后端的情况下创建登录和注册页面(ReactJS+Redux)
- 如何在ReactJS + Redux中检查数组中的每个对象
- Reactjs + redux :在不同组件之间共享化简器
- 如何正确处理双嵌套数组的状态(ReactJS + Redux)
- ReactJS Redux Live editing
- 为什么不是't反应路由器链路工作(ReactJS+Redux)