使用createSlice中的reducer从redux状态的数组中删除元素

4

我已经思考了一段时间,但是仍无法解决问题,希望能得到任何帮助。

我正在使用React与Redux Toolkit,但我在尝试从UI中删除一个“todo”时遇到了困难,即使Redux的响应是按预期进行的。在Redux Developer Tools中removeTodo按预期工作,从todos数组状态中删除了一个todo,但React没有跟随,因此我的UI也没有相应更改。我的addTodo动作在React和Redux中都按预期工作。

当我点击调用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.

需要翻译的内容:

需要注意的是,只有当我的todos数组状态长度大于0时,我才在我的TodoList组件中进行渲染,因为我不希望在没有待办事项时渲染该组件。我对React和Redux还很陌生,也许有一个非常简单的解决方案,但是从我能够理解的来看,当调用removeTodo时,todos数组状态被完全删除,而不仅仅是返回那些与传入的id不相等的id。这就是为什么我认为我得到的错误告诉我无法读取未定义的.length,因为现在我的todos状态为空。

我取消了对TodoList进行渲染所需的todos.length大于0的要求,但然后我收到了错误,即在TodoList中无法读取未定义的.map(我的todos状态),这对我来说证实了我的整个todos状态似乎正在被删除。

这是我的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;

还有我的store.js

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

export default configureStore({
  reducer: {
    todos: todosSliceReducer,
  },
});
1个回答

5
你能按照以下所示更新removeTodo并查看结果吗?
removeTodo: (state, action) => {
      // console.log(state.todos);

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

这确实有效,我感到有点傻,因为我之前没有尝试过这个方法,但我认为在使用createSlice时不需要先复制先前的状态,因为它可以帮助我们安全地“变异”状态,这是通过Immer实现的。或者我错了吗? - Jelliott
我的错误,我没有使用createSlice,如果您不需要传递现有状态,可以尝试state.todos = state.todos.filter(item => item.id !== id),类似于在addTodo中的state.todos.push({id,task})。Push修改现有数组,而filter会创建一个新的数组。 - Anish Antony
我已经更新了答案,请告诉我它的工作情况。 - Anish Antony
是的,那个方法可行。我之前没有意识到在创建新状态时需要重新定义状态(使用“state.todos =”),而不是对现有状态进行突变。谢谢! - Jelliott
@Jelliott 潜在的问题在于你原来的 removeTodo reducer 返回的是新的 state.todos 数组作为整个状态,而不是修改 state.todos - Code-Apprentice

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接