4

My initialState look like below:

const initialState: Todos = {
  todos: [
    {
      id: 1,
      completed: true,
      title: "setup redux",
      note: "test1",
      steps: [
        { id: 1, completed: true, title: "test2" },
        { id: 2, completed: false, title: "test2" }
      ]
    },
    {
      id: 2,
      completed: false,
      title: "using redux on typescript",
      note: "test3"
    }
  ]
};

So right now my problem is how do I update "step" property in Redux toolkit? I have done some research know there's a way to update nested objects by destructing my objects, but the thing is that if I want to update my "step" property, I have to know todo[id] and steps[id] to destruct my objects, but I think redux is not allowed to pass two action.payload at a time, so I wonder how to do this.

For other CRUD actions like delete and add , I create a new Todo and used updateTodo like below,but because I can't alter what's inside state without dispatch so I can't use this method to update steps (some update like toggle), and also I don't think this is a good idea for replacing nested objects.

//Redux-todoSlice.tsx
updateTodo : (state,action:PayloadAction<Todo>)=>{
      state.todos = state.todos.map( (todo)=>
        todo.id === action.payload.id ? action.payload : todo
      )
    }

//------------In my components

const Rightbar: React.FC<rightbarProps> = ({ currentTodoId }) => {
const todos  = useSelector((state:RootState)=>
    state.todo.todos.filter((todo=>todo.id === currentTodoId))
);
 const currentTodo = todos[0]
#....somecode
const deleteStep = (id:number)=>{

     if (currentTodo.steps!==undefined){
      const newSteps = currentTodo?.steps.filter((step)=> step.id!==id)
      const newTodo:Todo = {...currentTodo,steps:newSteps}
      dispatch(updateTodo(newTodo)) 
     }
  }

So do anyone know is there a better approach for this?

Ryan Le
  • 7,708
  • 1
  • 13
  • 23
Kai021195
  • 653
  • 3
  • 13
  • 26
  • You actually want to prefer replacing objects instead of [mutating](https://stackoverflow.com/questions/214714/mutable-vs-immutable-objects) them. You're in control of how the action `payload` looks. It is also possible to use a `payload` like this: `{ todoId: 1, stepId: 1, todo }`. Regarding the `deleteStep`, you don't want to create the new todos in your component/container, but do this with a separate action/reducer. – Chris Aug 30 '21 at 06:47
  • @Chris in Redux Toolkit you actually can (and should) write mutating reducer code: https://redux-toolkit.js.org/usage/immer-reducers – phry Aug 30 '21 at 07:46
  • @phry ah, I didn't know that redux-toolkit is using Immer. – Chris Aug 30 '21 at 07:58

1 Answers1

3

You can use an object with multiple properties as payload.

You would do something like

completeStep(state, action: PayloadAction<{ todoId: string, stepId: string }>) {
  const todo = state.todos.find(todo => todo.id == payload.action.todoId)
  if (todo) {
    const step = todo.steps.find(step => step.id == payload.action.stepId);
    if (step) {
      step.completed = true
    }
  }
}

Further reading: https://redux-toolkit.js.org/usage/immer-reducers

phry
  • 35,762
  • 5
  • 67
  • 81
  • after digging the comments above , I actually figure out by myself, and that's exactly like your answer, thanks anyway! – Kai021195 Aug 30 '21 at 08:19