You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将React类组件重构为使用Context的函数组件求助

重构React Beautiful DND类组件为函数组件(结合Context)

我帮你梳理下重构的核心步骤和关键代码,搞定这个问题其实就是把类组件的内部状态管理转移到Context,同时适配React Beautiful DND的API到函数组件:


1. 基础准备:导入依赖与Context

首先,在你的函数组件里导入需要的Hooks和Context,还有React Beautiful DND的核心组件:

import React, { useContext, useCallback, useMemo } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { CardContext } from './path-to-your-card-context'; // 替换成你的Context实际路径

2. 移除内部State,从Context获取状态与调度器

原来类组件里维护的列、卡片状态,全部换成从CardContext中获取,同时拿到dispatch用来更新全局状态:

const Board = () => {
  const { state, dispatch } = useContext(CardContext);
  // 这里的state需包含你的columns和cards数据,比如state.columns、state.cards

3. 重构拖拽结束回调(核心!)

把原来类组件里onDragEnd方法的逻辑,改成通过dispatch触发Reducer的动作,不再更新组件内部state。用useCallback包裹这个回调,避免每次渲染都创建新函数,影响拖拽性能:

const onDragEnd = useCallback((result) => {
    const { source, destination, draggableId } = result;

    // 如果拖拽没有有效目标或位置未变化,直接返回
    if (!destination) return;
    if (source.droppableId === destination.droppableId && source.index === destination.index) return;

    // 分两种情况处理:同一列内排序 / 跨列移动
    if (source.droppableId === destination.droppableId) {
      // 同一列内排序,分发REORDER_CARD动作
      dispatch({
        type: 'REORDER_CARD',
        payload: {
          columnId: source.droppableId,
          sourceIndex: source.index,
          destinationIndex: destination.index,
          cardId: draggableId
        }
      });
    } else {
      // 跨列移动,分发MOVE_CARD动作
      dispatch({
        type: 'MOVE_CARD',
        payload: {
          sourceColumnId: source.droppableId,
          destinationColumnId: destination.droppableId,
          sourceIndex: source.index,
          destinationIndex: destination.index,
          cardId: draggableId
        }
      });
    }
  }, [dispatch]);

4. 调整Draggable/Droppable的渲染逻辑

用useMemo缓存渲染的列和卡片列表,避免因为状态变化导致不必要的重渲染:

const renderedColumns = useMemo(() => {
    return Object.values(state.columns).map((column) => {
      // 获取当前列的卡片列表
      const columnCards = column.cardIds.map(cardId => state.cards[cardId]);

      return (
        <div key={column.id} className="column">
          <h3>{column.title}</h3>
          <Droppable droppableId={column.id}>
            {(provided) => (
              <div
                {...provided.droppableProps}
                ref={provided.innerRef}
                className="column-content"
              >
                {columnCards.map((card, index) => (
                  <Draggable key={card.id} draggableId={card.id} index={index}>
                    {(provided) => (
                      <div
                        ref={provided.innerRef}
                        {...provided.draggableProps}
                        {...provided.dragHandleProps}
                        className="card"
                      >
                        {card.content}
                      </div>
                    )}
                  </Draggable>
                ))}
                {provided.placeholder}
              </div>
            )}
          </Droppable>
        </div>
      );
    });
  }, [state.columns, state.cards]);

5. 在Card Reducer中处理拖拽动作

最后,确保你的CardContext对应的Reducer里,有处理上述两个动作的case,把原来类组件里的state更新逻辑迁移过来:

const cardReducer = (state, action) => {
  switch (action.type) {
    case 'REORDER_CARD': {
      const { columnId, sourceIndex, destinationIndex, cardId } = action.payload;
      const column = state.columns[columnId];
      const newCardIds = Array.from(column.cardIds);
      // 移除原位置的卡片,插入到目标位置
      newCardIds.splice(sourceIndex, 1);
      newCardIds.splice(destinationIndex, 0, cardId);

      const newColumn = {
        ...column,
        cardIds: newCardIds
      };

      return {
        ...state,
        columns: {
          ...state.columns,
          [columnId]: newColumn
        }
      };
    }

    case 'MOVE_CARD': {
      const { sourceColumnId, destinationColumnId, sourceIndex, destinationIndex, cardId } = action.payload;
      // 更新源列的卡片列表
      const sourceColumn = state.columns[sourceColumnId];
      const newSourceCardIds = Array.from(sourceColumn.cardIds);
      newSourceCardIds.splice(sourceIndex, 1);
      const newSourceColumn = {
        ...sourceColumn,
        cardIds: newSourceCardIds
      };

      // 更新目标列的卡片列表
      const destinationColumn = state.columns[destinationColumnId];
      const newDestinationCardIds = Array.from(destinationColumn.cardIds);
      newDestinationCardIds.splice(destinationIndex, 0, cardId);
      const newDestinationColumn = {
        ...destinationColumn,
        cardIds: newDestinationCardIds
      };

      return {
        ...state,
        columns: {
          ...state.columns,
          [sourceColumnId]: newSourceColumn,
          [destinationColumnId]: newDestinationColumn
        }
      };
    }

    // 其他已有的action case...
    default:
      return state;
  }
};

关键注意点

  • 一定要用useCallback包裹onDragEnd,否则每次组件渲染都会生成新的回调函数,React Beautiful DND可能会出现拖拽异常。
  • 用useMemo缓存渲染的列和卡片列表,减少不必要的重渲染,提升拖拽流畅度。
  • 确保Context的状态是不可变更新的(Reducer里都是返回新对象,不修改原状态),否则组件可能不会触发重渲染。

内容的提问来源于stack exchange,提问作者Paul A

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:35:26