Im try to make a todo list with React and Redux Toolkit, i searching for this error so many times but still don’t have a answer to solve a problem.
I think my action.payload is well defined. removeItem, toggleItem is worked fine, but i can only using one of them. When i add toggleItem function, i received the following error
Cannot set properties of undefined (setting 'check')
Here are my full code on stackblitz https://stackblitz.com/edit/react-ts-bqmjz1?file=redux%2Ffeatures%2FtodoSlice.ts
createSlice.ts file
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
const todoSlice = createSlice({
name: 'todo',
initialState: [],
reducers: {
addItem: (state, action: PayloadAction<Todo>) => {
const newItem = {
id: new Date().getTime().toString(),
title: action.payload.title,
check: action.payload.check,
};
state.unshift(newItem);
},
removeItem: (state, action: PayloadAction<Todo>) => {
return state.filter((item) => item.id !== action.payload.id);
},
//Toggle check
toggleCheck: (state, action: PayloadAction<Todo>) => {
const index = state.findIndex((item) => item.id === action.payload.id);
state[index].check = action.payload.check;
},
},
});
export const { addItem, removeItem, toggleCheck } = todoSlice.actions;
export default todoSlice.reducer;
**TodoItem.tsx **
const TodoItem = ({ item }) => {
const dispatch = useDispatch();
const handleRemove = () => {
dispatch(removeItem({ id: item.id }));
};
const handleToggle = () => {
dispatch(toggleCheck({ id: item.id, check: !item.check }));
};
return (
<li
className={`border border-2 list-group-item my-2 ${
item.check ? 'border-primary text-primary' : 'border-dark'
}`}
onClick={handleToggle}
>
<div className="d-flex justify-content-between align-items-center">
<h4>{item.title}</h4>
<i onClick={handleRemove} className="fas fa-times text-danger"></i>
</div>
</li>
);
};
export default TodoItem;
>Solution :
When you click the cross icon, the click event is also propagated to the parent element li. So, when you click on cross, first handleRemove will be called. And then, handleToggle will be called. But, by the time handleToggle is called, the item is already removed from the list and hence, you are not able to change the check property of the item as const index = state.findIndex((item) => item.id === action.payload.id); will be -1 in the toggleCheck function and there is no item at index -1.
To fix this, you can do the following in handleRemove function:
const handleRemove = (e) => {
e.stopPropagation();
dispatch(removeItem({ id: item.id }));
};
So, if you click on cross icon, it won’t propagate the click event to the parent li event and handleToggle won’t be invoked.