简体   繁体   中英

How do you define a state of an object inside an array of an array in a reducer (ReactJS + Redux)?

So inside my reducer, I have an array of objects called 'todos', and an object of 'todos' has a state which is also an array of objects, called 'comments'. And inside each of 'comments' array, I would like to define a string state 'commentText', but I can't seem to figure out how to do so. Any help would be greatly appreciated.

Following is an example of what I would like to achieve:

let todoReducer = function(todos = [], action){
    switch(action.type){
        case 'ADD_TODO':
            return [{
                comments:[{
                    commentText: action.commentText
                }]
            }, ...todos]

        case 'CREATE_COMMENT_ARRAY':
            return [{
                commentText: action.eventValue
            ], ...todos.comments] //Referencing todos.comments as 'comments' array of objects of an object of 'todos' array. Would like to directly update if possible and build up 'comments' array.

       default:
        return todos
    }
}
export default todoReducer

NEW EDIT**:

case 'UPDATE_COMMENT':
  return todos.map(function(todo){
    if(todo.id === action.id){
      //want to add a new a 'comment' object to the todo's 'comments' array
    //Something like the following:
        todo.comments: [{
            commentText: action.commentText
        }, ...todo.comments]
    }
  })

This sounds like a great use case for the .map() Array method.

Assuming your data looks like this:

var todos = [
  {
    id: 1,
    title: 'Todo 1 Title',
    body: 'Todo 1 Body',
    date: 1425364758,
    comments: [
      {
        id: 1,
        commentorId: 42069,
        text: 'Todo 1, Comment 1 Text',
        date: 1425364758
      },
      {
        id: 2,
        commentorId: 42069,
        text: 'Todo 1, Comment 2 Text',
        date: 1425364758
      },
      {
        id: 3,
        commentorId: 42069,
        text: 'Todo 1, Comment 3 Text',
        date: 1425364758
      }
    ]
  },
  {
    id: 2,
    title: 'Todo 2 Title',
    body: 'Todo 2 Body',
    date: 1425364758,
    comments: [
      {
        id: 1,
        commentorId: 42069,
        text: 'Todo 2, Comment 1 Text',
        date: 1425364758
      }
    ]
  },
  {
    id: 3,
    title: 'Todo 3 Title',
    body: 'Todo 3 Body',
    date: 1425364758,
    comments: [
      {
        id: 1,
        commentorId: 42069,
        text: 'Todo 3, Comment 1 Text',
        date: 1425364758
      },
      {
        id: 2,
        commentorId: 42069,
        text: 'Todo 3, Comment 2 Text',
        date: 1425364758
      }
    ]
  }
];

When updating a comment, you'd need to pass in a todoId and a commentId so you know what to look for. When adding a comment, you'd only need to pass in a todoId so you know which todo to update:

const todoReducer = (todos = [], action) => {
  switch(action.type) {
    case 'ADD_TODO':
      return [
        action.todo,
        ...todos
      ];
    case 'ADD_COMMENT':
      const { todoId, comment } = action;

      // Map through all the todos. Returns a new array of todos, including the todo with a new comment
      return todos.map(todo => {
        // Look for the todo to add a comment to
        if (todo.id === todoId) {
          // When the todo to update is found, add a new comment to its `comments` array
          todo.comments.push(comment);
        }
        // Return the todo whether it's been updated or not
        return todo;
      });
    case 'UPDATE_COMMENT':
      const { todoId, commentId, commentText } = action;

      // Map through all the todos. Returns a new array of todos, including the todo with the updated comment
      return todos.map(todo => {
        // First find the todo you want
        if (todo.id === todoId) {
          // Then iterate through its comments
          todo.comments.forEach(comment => {
            // Find the comment you want to update
            if (comment.id === commentId) {
              // and update it
              comment.text = commentText;
            }
          });
        }
        // Return the todo whether it's been updated or not
        return todo;
      });
    default:
      return todos;
  }
};
export default todoReducer;

As for your payloads, you can make them whatever you want, and they'll be created in your action creator. For example, here's an implementation of ADD_TODO that gives the todo a unique ID, timestamps it, and adds an empty comments array before firing the action:

import uuid from 'node-uuid';
const addTodo = ({title, body}) => {
  const id = uuid.v4();
  const date = new Date().getTime();
  return {
    type: 'ADD_TODO',
    todo: {
      id,
      title,
      body,
      date,
      comments: []
    }
  };
};

Your ADD_COMMENT action creator might look something like this:

import uuid from 'node-uuid';
const addComment = ({todoId, commentorId, commentText}) => {
  const id = uuid.v4();
  const date = new Date().getTime();
  return {
    type: 'ADD_COMMENT',
    todoId,
    comment: {
      id,
      commentorId,
      date,
      text: commentText
    }
  };
};

This is untested but hopefully gives you an idea.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM