HDallakyan
HDallakyan

Reputation: 748

How to add object at array index in reducer

I have store with arrays (store.calendar[0].todos[{title: title, etc...}])

    0: {todos: Array(0)}
    1: {todos: Array(0)}
    2: {todos: Array(0)}
    3: {todos: Array(0)}
    4: {todos: Array(0)}

I need to add action object to the index todos of array: I have tried with this reducer, but I get an error:

state.calendar[newTodo.day].concat is not a function

My reducer:

let initialState = { calendar: []}

for (let i = 1; i <= 30; i++) {
  initialState.calendar.push({ todos: []});
}

const todosReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TODO:
      const newTodo = action.todoItem;
      const newStore = {...state,
            todos: state.calendar[newTodo.day].concat(newTodo)};
      return newStore;
    default:
      return state
  }
}

export default todosReducer;

My action:

export const addTodoAction = (todoItem) => {
  return {
    type: ADD_TODO,
    todoItem
  }
}

My add todo function:

const handleSaveTodo = () => {
    props.addTodo({ 
      day: 5,
      title: trackInput.value,
      description: trackTextarea.value,
      completed: false
    });
}

Upvotes: 2

Views: 1045

Answers (3)

jo_va
jo_va

Reputation: 13964

You need to change your state in a totally immutable way.

For this, you have to copy the calendar array, the day you are updating in this calendar and the todo list you are appending to.

First get the day and todoItem, you can use destructuring:

const { todoItem } = action;
const { day } = todoItem;

Then copy your calendar, you can use the spread syntax for that:

const calendar = [...state.calendar];

Then update the relevant day with a copy for that day, and append the new todo to the list of todos:

calendar[day] = { ...calendar[day], todos: [...calendar[day].todos, todoItem] };

Then return the updated state:

return { ...state, calendar };

Here is an example:

const ADD_TODO = 'add-todo';

const initialState = { calendar: Array.from({ length: 30 }, (_, i) => ({ todos: [] })) };

const todosReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TODO:
      const { todoItem } = action;
      const { day } = todoItem;
      const calendar = [...state.calendar];
      calendar[day] = { ...calendar[day], todos: [...calendar[day].todos, todoItem] };
      return { ...state, calendar };
    default:
      return state
  }
}

let state = initialState;

state = todosReducer(state, { type: ADD_TODO, todoItem: { day: 0, title: 'todo day 1' } });
state = todosReducer(state, { type: ADD_TODO, todoItem: { day: 1, title: 'todo day 2' } });
state = todosReducer(state, { type: ADD_TODO, todoItem: { day: 2, title: 'todo day 3' } });
state = todosReducer(state, { type: ADD_TODO, todoItem: { day: 2, title: 'second todo day 3' } });

console.log(state.calendar.slice(0, 3));

Upvotes: 2

Fenny
Fenny

Reputation: 65

you could try this:

Reducer:

const initialState = { 
  calendar: Array.from({length: 30}, () => ({ todos: [] }))
}

const todosReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TODO:
      const { todoItem } = action;
      const newCalendar = [...state.calendar];
      newCalendar[todoItem].todos.push(todoItem);
      
      return {
        ...state,
        calendar: newCalendar
      }
    default:
      return state
  }
}

export default todosReducer;

Upvotes: -1

ifiore
ifiore

Reputation: 470

Try state.calendar[newTodo.day].todo.concat(newTodo) instead. I think you're trying to .concat() onto the object {todo: Array(0)} rather than the array within.

Upvotes: 0

Related Questions