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.
commentTextof 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.