Next.js 12.3.1中react-beautiful-dnd 13.1.1无法拖拽求助
问题:Next.js 12.3.1 中 react-beautiful-dnd 无法拖拽,无抓取光标
使用Next.js 12.3.1开发Web应用,借助react-beautiful-dnd 13.1.1实现简单垂直可拖拽列表,但所有项目都无法拖拽,鼠标悬停仅显示普通指针而非抓取光标,已尝试在_document.js中调用resetServerContext但无效果,项目编译无报错。
代码如下:
import React, { useState } from "react"; import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"; export default function BigOlList() { const defaultList = ["A", "B", "C", "D", "E"]; // React state to track order of items const [itemList, setItemList] = useState(defaultList); // Function to update list on drop const handleDrop = (droppedItem) => { // Ignore drop outside droppable container if (!droppedItem.destination) return; var updatedList = [...itemList]; // Remove dragged item const [reorderedItem] = updatedList.splice(droppedItem.source.index, 1); // Add dropped item updatedList.splice(droppedItem.destination.index, 0, reorderedItem); // Update State setItemList(updatedList); }; return ( <div className="App"> <DragDropContext onDragEnd={handleDrop}> <Droppable droppableId="list-container"> {(provided) => ( <div className="list-container" {...provided.droppableProps} ref={provided.innerRef}> {itemList.map((item, index) => ( <Draggable key={item} draggableId={item} index={index}> {(provided) => ( <div className="item-container" {...provided.dragHandleProps} {...provided.draggableProps} ref={provided.innerRef} > {item} </div> )} </Draggable> ))} {provided.placeholder} </div> )} </Droppable> </DragDropContext> </div> ); }
解决方案
1. 解决Next.js SSR的Hydration不匹配问题
react-beautiful-dnd依赖DOM测量,SSR环境下会导致客户端与服务端渲染结果不一致,仅在_document.js调用resetServerContext不够,需确保拖拽组件仅在客户端渲染:
方式一:用Next.js dynamic导入禁用SSR
import dynamic from 'next/dynamic'; // 动态导入拖拽组件并禁用服务端渲染 const DragDropContext = dynamic( () => import('react-beautiful-dnd').then(mod => mod.DragDropContext), { ssr: false } ); const Droppable = dynamic( () => import('react-beautiful-dnd').then(mod => mod.Droppable), { ssr: false } ); const Draggable = dynamic( () => import('react-beautiful-dnd').then(mod => mod.Draggable), { ssr: false } );
方式二:组件内用状态控制客户端渲染时机
import React, { useState, useEffect } from "react"; import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"; export default function BigOlList() { const [isClient, setIsClient] = useState(false); const defaultList = ["A", "B", "C", "D", "E"]; const [itemList, setItemList] = useState(defaultList); useEffect(() => { // 客户端挂载后标记为已就绪 setIsClient(true); }, []); const handleDrop = (droppedItem) => { if (!droppedItem.destination) return; const updatedList = [...itemList]; const [reorderedItem] = updatedList.splice(droppedItem.source.index, 1); updatedList.splice(droppedItem.destination.index, 0, reorderedItem); setItemList(updatedList); }; // 客户端未就绪前不渲染拖拽组件 if (!isClient) return null; return ( <div className="App"> <DragDropContext onDragEnd={handleDrop}> <Droppable droppableId="list-container"> {(provided) => ( <div className="list-container" {...provided.droppableProps} ref={provided.innerRef}> {itemList.map((item, index) => ( <Draggable key={item} draggableId={item} index={index}> {(provided) => ( <div className="item-container" {...provided.draggableProps} ref={provided.innerRef} > {/* 单独设置拖拽手柄,明确触发区域 */} <div {...provided.dragHandleProps} className="drag-handle">☰</div> {item} </div> )} </Draggable> ))} {provided.placeholder} </div> )} </Droppable> </DragDropContext> </div> ); }
2. 调整Props顺序与拖拽触发区域
- 确保
draggableProps先于dragHandleProps传递,避免props覆盖 - 单独设置拖拽手柄(
dragHandleProps),不要直接绑定在整个item容器上,明确拖拽触发区域 - 添加基础样式确保光标显示:
.item-container { padding: 8px 12px; margin: 4px 0; border: 1px solid #eee; border-radius: 4px; display: flex; align-items: center; gap: 8px; } .drag-handle { cursor: grab; /* 设置抓取光标 */ user-select: none; /* 避免拖拽时选中文本 */ } .drag-handle:active { cursor: grabbing; /* 拖拽中的光标样式 */ }
3. 验证resetServerContext的正确调用
在_document.js中确保服务端渲染完成后重置上下文:
import Document, { Html, Head, Main, NextScript } from 'next/document'; import { resetServerContext } from 'react-beautiful-dnd'; class MyDocument extends Document { static async getInitialProps(ctx) { const initialProps = await Document.getInitialProps(ctx); resetServerContext(); // 服务端渲染后重置上下文 return initialProps; } render() { return ( <Html> <Head /> <body> <Main /> <NextScript /> </body> </Html> ); } } export default MyDocument;
内容的提问来源于stack exchange,提问作者Gabriel
相关产品推荐
相关产品推荐

