Skip to content
Advertisement

Delete element from array in redux state using a reducer in createSlice

I’ve been scratching my brain for a while now with this one and any help would be appreciated.

I am using React with Redux Toolkit and I’m struggling to get React to remove a ‘todo’ from my UI even though Redux is responding as expected. In Redux Developer Tools removeTodo works as expected, removing a todo from the todos array state, but React doesn’t follow and therefore my UI doesn’t change with it. My addTodo action works as expected in both React and Redux.

My current code provides me with the following error when I click the button that calls the dispatch of removeTodo.

TypeError: Cannot read property 'length' of undefined
App
C:/Users/joeee/Documents/redux-middleware/src/app/App.js:13
  10 | 
  11 |  return (
  12 |    <div style={divStyles}>
> 13 |      <TodosForm />
     | ^  14 |      {todos.length > 0 && <TodoList />}
  15 |    </div>
  16 |  )
View compiled
▶ 19 stack frames were collapsed.

It should be noted that I am only rendering in my TodoList component when my todos array state has a length > 0 as I don’t want the component rendered in when there are no todos. I am new to React and Redux and there is probably a very simple solution but from what I can decipher is that when removeTodo is called, the todos array state is being removed completely rather than just returning those with id’s not equal to the id passed in. This is why I assume the error I am getting is telling me it can’t read the .length of undefined because my todos state is now empty.

I removed the requirement for the todos.length needing to be greater than 0 for TodoList to render but then I got the error that it couldn’t read .map of undefined (my todos state) in TodoList which to me reinforces that my whole todos state seems to be being deleted.

Here is my todosSlice:

import { createSlice } from '@reduxjs/toolkit';

export const todosSlice = createSlice({
  name: 'todos',
  initialState: {
    todos: [],
  },
  reducers: {
    addTodo: (state, action) => {
      const { id, task } = action.payload; 

      state.todos.push({ id, task })
    },
    removeTodo: (state, action) => {
      // console.log(state.todos);

      const { id } = action.payload; 
      // console.log(id);
      
      return state.todos.filter(item => item.id !== id);
      
    }
  },
});

export const selectTodos = state => state.todos.todos; 

export const { addTodo, removeTodo } = todosSlice.actions; 
export default todosSlice.reducer; 

App.js:

import React from 'react';
import { useSelector } from 'react-redux'; 
import TodosForm from '../components/TodosForm';
import TodoList from '../components/TodoList';
import { selectTodos } from '../features/todosSlice';

export const App = () => {
  const todos = useSelector(selectTodos);
  // console.log(todos.length);

  return (
    <div style={divStyles}>
      <TodosForm />
      {todos.length > 0 && <TodoList />}
    </div>
  )
}

export default App;

TodoList.js

import React from 'react';
import { useSelector } from 'react-redux';
import { selectTodos } from '../features/todosSlice';
import Todos from './Todos';

const TodoList = () => {

  const todos = useSelector(selectTodos);
  // console.log(todos);

  return (
    <div style={divStyles}>
      <h3 style={headerStyles}>Your Todos: </h3>
      
      {todos.map(todo => <Todos key={todo.id} task={todo.task} id={todo.id} />)}
    </div>
  )
}

export default TodoList

Todos.js

import React from 'react';
import { useDispatch } from 'react-redux'; 
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { faEdit } from '@fortawesome/free-solid-svg-icons'
import { removeTodo } from '../features/todosSlice';

const Todos = ({ task, id }) => {
  const dispatch = useDispatch();

  const handleDeleteClick = () => {
    dispatch(removeTodo({id: id}));
  }

  return (
    <div style={divStyles}>
      <li style={listStyles}>{task}</li>
      <div>
        <button className="faEditIcon" style={btnStyles}><FontAwesomeIcon icon={faEdit}/></button>
        <button className="faDeleteIcon" style={btnStyles} onClick={handleDeleteClick}><FontAwesomeIcon icon={faTrashAlt}/></button>
      </div>
    </div>
  )
}

export default Todos;

And my store.js

import { configureStore } from '@reduxjs/toolkit'; 
import todosSliceReducer from '../features/todosSlice'; 

export default configureStore({
  reducer: {
    todos: todosSliceReducer,
  },
});

Advertisement

Answer

Can you update removeTodo as below and see.

removeTodo: (state, action) => {
      // console.log(state.todos);

      const { id } = action.payload; 
      // console.log(id);
      
state.todos = state.todos.filter(item => item.id !== id)
      
    }
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement