学习React-DnD:实现多任务项拖动-维护多任务项数组

一、功能承接与需求概述

上一篇文档中,我们已完成单个任务项的拖动排序功能。本次迭代的核心需求是实现多个任务项的批量选中,具体需达成以下目标:

  • 扩展状态管理,支持存储选中的任务项集合
  • 新增选中、取消选中任务项的操作逻辑
  • 在任务项组件中关联单选框与选中状态的交互

二、核心状态扩展(TodoProvider)

要实现多任务选中与拖拽,首先需要在全局状态中新增"选中任务集合"字段,并同步更新上下文供组件调用。

2.1 初始化状态修改

initialState中添加selectedTodos数组,用于存储当前选中的任务项,同时确保任务项模型的完整性:

js 复制代码
const initialState = {
  todos: [
    { id: 1, text: 'Learn React', completed: false, selected: false },
    { id: 2, text: 'Build a Todo App', completed: false, selected: false },
    { id: 3, text: 'Build a Demo', completed: true, selected: false },
    { id: 4, text: 'Fix a Bug', completed: false, selected: true },

  ],
  // 当前选中的任务数组
  selectedTodos: [{ id: 4, text: 'Fix a Bug', completed: false, selected: true }],
};

2.2 上下文同步更新

将新增的selectedTodos状态注入上下文,确保子组件能获取到选中任务集合:

js 复制代码
// TodoProvider内部的上下文值配置
const contextValue = {
  todos: state.todos,
  selectedTodos: state.selectedTodos,
  ...actions,
};

三、核心操作逻辑实现(Reducer)

基于需求新增两个核心操作类型:ADD_SELECT_TODOS(添加选中任务)、REMOVE_SELECT_TODOS(移除选中任务),以下是完整实现逻辑。

3.1 操作类型常量定义(建议单独维护)

js 复制代码
// ActionTypes.js
export const ActionTypes = {
  // 原有操作类型...
  ADD_SELECT_TODOS: 'ADD_SELECT_TODOS',
  REMOVE_SELECT_TODOS: 'REMOVE_SELECT_TODOS',
};

3.2 添加选中任务(ADD_SELECT_TODOS)

通过任务ID查找目标任务,确保任务存在且未被选中后,添加到selectedTodos集合中,避免重复选中:

js 复制代码
// ADD_SELECT_TODOS
case ActionTypes.ADD_SELECT_TODOS:
  {
    // 找到要添加的todo对象
    const todoToAdd = state.todos.find(todo => todo.id === action.payload.id);
    // 确保todo存在且尚未被选中(避免重复添加)
    if (todoToAdd && !state.selectedTodos.some(todo => todo.id === todoToAdd.id)) {
      return {
        ...state,
        selectedTodos: [...state.selectedTodos, todoToAdd],
      };
    }
    return state;
  }

3.3 移除选中任务(REMOVE_SELECT_TODOS)

根据任务ID从selectedTodos集合中过滤掉目标任务:

js 复制代码
// REMOVE_SELECT_TODOS
case ActionTypes.REMOVE_SELECT_TODOS:
  return {
    ...state,
    selectedTodos: state.selectedTodos.filter(todo => todo.id !== action.payload.id),
  };

四、任务项组件交互(TodoItem)

在TodoItem组件中,通过Ref绑定单选框,并在单选框状态变化时,触发选中/取消选中的操作,实现视图与状态的同步。

4.1 组件核心逻辑

js 复制代码
import { useContext, useRef } from 'react';
import useTodoContext from '@/context/TodoContext/useTodoContext';

const TodoItem = ({ todo }) => {
  const { addSelectTodos, removeSelectTodos } = useTodoContext();
  // 用Ref绑定单选框元素,便于后续操作(如主动获取状态)
  const checkboxRef = useRef(null);

  // 单选框状态变化处理函数
  const handleCheckboxChange = (e) => {
    toggleSelected(todo.id);
    if (e.target.checked) {
      // 选中:调用添加选中任务的action
      addSelectTodos(todo.id);
    } else {
      // 取消选中:调用移除选中任务的action
      removeSelectTodos(todo.id);
    }
  };

  return (
    <div className={`todo-item${isDragging ? ' isDragging' : ''}`} ref={divRef}>
      <div className="todo-item-content">
        <input
          type="checkbox"
          id={`todo-${todo.id}`}
          checked={todo.selected}
          onChange={handleCheckboxChange}
          className="todo-checkbox"
          ref={checkboxRef}
        />
      </div>
      {/* 原有单个任务拖拽相关逻辑 */}
    </div>
  );
};

export default TodoItem;

注意:仅保留必需内容,不包含TodoItem组件的所有内容。

4.2 关键交互说明

  • Ref绑定 :通过checkboxRef可在需要时主动控制单选框(如全选/取消全选功能),提升组件灵活性。
  • 状态双向绑定 :单选框的checked属性直接绑定任务项的selected状态,确保视图与全局状态一致。
  • 操作触发 :状态变化时通过上下文获取的addSelectTodosremoveSelectTodos方法更新全局状态,实现跨组件状态同步。

五、配套Action函数实现

为了让组件更便捷地调用上述操作,需在TodoProvider中定义对应的action函数,并注入上下文:

javascript 复制代码
const actions = {
  // 原有action...
  
    // 添加选中任务
    addSelectTodos: (id) => {
      dispatch({
        type: ActionTypes.ADD_SELECT_TODOS,
        payload: { id },
      });
    },

    // 移除选中任务
    removeSelectTodos: (id) => {
      dispatch({
        type: ActionTypes.REMOVE_SELECT_TODOS,
        payload: { id },
      });
    },
    
};

六、测试要点

  1. 单个任务选中/取消:勾选单选框后,selectedTodos应同步增减,任务项selected状态正确。
  2. 多个任务选中:选中多个任务后,selectedTodos应包含所有选中项,无重复数据。
相关推荐
不务正业的前端学徒7 小时前
手写简单的call bind apply
前端
jump_jump7 小时前
Ripple:一个现代的响应式 UI 框架
前端·javascript·前端框架
用户904706683577 小时前
Nuxt css 如何写?
前端
神秘的猪头7 小时前
🎨 CSS 这种“烂大街”的技术,怎么在 React 和 Vue 里玩出花来?—— 模块化 CSS 深度避坑指南
css·vue.js·react.js
夏天想7 小时前
element-plus的输入数字组件el-input-number 显示了 加减按钮(+ -) 和 小三角箭头(上下箭头),怎么去掉+,-或者箭头
前端·javascript·vue.js
0思必得07 小时前
[Web自动化] Selenium基础介绍
前端·python·selenium·自动化·web自动化
Filotimo_7 小时前
前端.d.ts文件作用
前端
进击的野人7 小时前
Vue 3 响应式数据解构:toRef 与 toRefs 的深度解析
前端·vue.js·前端框架
ohyeah7 小时前
CSS 作用域隔离实战:React、Vue 与 Styled Components 的三种范式
前端
二哈喇子!7 小时前
前端HTML、CSS、JS、VUE 汇总
开发语言·前端