0

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]
    }
  })
Jo Ko
  • 7,225
  • 15
  • 62
  • 120
  • [Updeep](https://github.com/substantial/updeep) makes it easy to perform updates of nested objects and arrays; I'd recommend taking a look at it. – Josh Kelley Jun 23 '16 at 22:27
  • http://stackoverflow.com/questions/37980109/react-redux-complex-deep-state-objects duplicate – xiaofan2406 Jun 23 '16 at 23:01
  • @xiaofan2406 I checked it out but I can't seem to grasp the concept well enough. If you don't could you provide an example according to what I provided? Thank you – Jo Ko Jun 23 '16 at 23:04
  • Are you trying to update the `commentText` of a particular Todo? If so, you'll probably need to give the Todos unique IDs so you can filter through the list of them to find the one you need to update. Otherwise there's no way to tell one Todo from another. – jaybee Jun 24 '16 at 00:33
  • @dannyid Yes that's exactly it! If you don't mind, could you show an example? I can't seem to get it to work... – Jo Ko Jun 24 '16 at 02:28

1 Answers1

0

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.

jaybee
  • 2,240
  • 14
  • 20
  • Thanks for the guide! Trying to still wrap my head around it. Just to clarify, how would the payloads be defined in action creators? – Jo Ko Jun 27 '16 at 20:52
  • Also, I would like to just create a 'comments' empty array in 'ADD_TODO' and not set values inside, and when UPDATE_COMMENT is activated, be able to continue to add a new comment to the initialized empty 'comments' array. – Jo Ko Jun 27 '16 at 20:55
  • I updated the original post with code, under NEW EDIT. – Jo Ko Jun 27 '16 at 21:22
  • Just checking to see if you were able to view the new comment. – Jo Ko Jun 29 '16 at 20:24
  • Hey @JoKo, check out my updated answer. I expounded a bunch. – jaybee Jul 01 '16 at 19:21