将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
相关产品推荐
相关产品推荐

