优秀的编程知识分享平台

网站首页 > 技术文章 正文

React Dnd 的理解(react dynamic)

nanyue 2024-08-06 18:02:27 技术文章 4 ℃

React Dnd 是 redux 作者 Dan 另外一个非常赞的项目, dnd 是 Drag and Drop 的意思,为什么他会开发react-dnd 的项目,这个拖放库解决什么问题,和html 原生Drap Drop API 有什么样的联系和不同,设计是否有独特之处?让我们带着这些问题了解一下React Dnd吧.

React Dnd 是什么?

Reat Dnd 是React 和 Redux 的核心作者 Dan Abramov 创造的一组React 高阶组件,可以在保持组件分离的前提下帮助构建复杂的拖放接口。非常适合做类似jira 之类的应用程序,其中拖动在应用程序的不同部分之间传递数据,并且组件会根据拖放更改其外观和应用程序状态.



React Dnd 的出发点

现有拖放插件的问题:

    • jquery 插件思维模式,直接改变DOM
    • 拖放状态改变的影响不仅限于 CSS 类这种改变,不支持更加自定义

Html5 拖放API 的问题

    • 不支持移动端
    • 拖动预览问题
    • 无法开箱即用

React Dnd 的需求

  • 默认使用Html 拖放API.
  • 不支持操作dom.
  • Dom 和拖放的源和目标解耦
  • 融入HTML5 拖放中获取类型匹配和数据传递的思想.

React Dnd 的特点:

专注拖拽,不提供现成组件

React DnD提供了一组强大的API,但它不包含任何现成组件,而是采用包裹使用者的组件并注入 props 的方式。 它比jQuery UI等更底层,专注于使拖放交互正确,而把视觉方面的效果例如坐标限制交给使用者处理。这其实是一种关注点分离的原则,例如React DnD不打算提供可排序组件,但是使用者可以基于它快速开发任何需要的自定义的可排序组件。

单向数据流

类似于 React 一样采取声明式渲染,并且像 redux 一样采用单向数据流架构,实际上内部使用了 Redux.

隐藏了平台底层API的问题

HTML5拖放API充满了陷阱和浏览器的不一致。 React DnD为您内部处理它们,因此使用者可以专注于开发应用程序而不是解决浏览器问题。

可扩展可测试

React DnD默认提供了HTML5拖放API封装,但它也允许您提供自定义的“后端(backend)”。您可以根据触摸事件,鼠标事件或其他内容创建自定义DnD后端。例如,内置的模拟后端允许您测试Node环境中组件的拖放交互。

React Dnd 基本用法:

下面使用 React Dnd 做的一个TaskCard 的应用:


TaskCard 拖拽源代码如下:

import { Box, Badge, Text, Flex } from '@chakra-ui/core';
import { ItemTypes } from '../utils/items';
import { useDrag } from 'react-dnd';

const TaskCard = props => {
	const [{ isDragging }, drag] = useDrag({
		item: {
			type: ItemTypes.CARD,
			id: props._id,
		},
		collect: monitor => ({
			isDragging: !!monitor.isDragging(),
		}),
	});

	return (
		<Box
			ref={drag}
			my='4'
			p={3}
			bg='gray.500'
			opacity={isDragging ? '0.5' : '1'}
			boxShadow='sm'
			w='100%'
			rounded='md'
			color='white'>
			<Flex justify='space-between' my='2'>
				<Text fontSize='lg' fontWeight='semibold'>
					{props.title}
				</Text>
				<Badge
					variantColor={props.category === 'Chores' ? 'green' : 'red'}
					h='100%'>
					{props.category}
				</Badge>
			</Flex>
			<Text textAlign='center' fontSize='md'>
				{props.details}
			</Text>
		</Box>
	);
};

export default TaskCard;

react-dnd 封装了一个 useDrag 的hook ,暴露出 isDragging,drag 的属性。

放置目标的代码如下:

import { Box } from '@chakra-ui/core';
import { useDrop } from 'react-dnd';
import { ItemTypes } from '../utils/items';
import { useContext } from 'react';
import { CardContext } from '../pages/tasks';

const BoxTarget = props => {
	const { markAsDone } = useContext(CardContext);

	const [{ isOver }, drop] = useDrop({
		accept: ItemTypes.CARD,
		drop: (item, monitor) => {
			console.log('item',item);
			return markAsDone(item.id);
		},
		collect: monitor => ({
			isOver: !!monitor.isOver(),
		}),
	});

	return (
		<Box
			ref={drop}
			m={2}
			p={3}
			boxShadow='sm'
			bg={isOver ? 'green.500' : 'green.200'}
			minH='200px'
			textAlign='center'
			w='100%'
			rounded='md'
			color='white'>
			{props.children}
		</Box>
	);
};

export default BoxTarget;

react-dnd 封装了一个 useDrop 的hook ,暴露出 drop 的属性。

源码地址: https://github.com/ms-mousa/Chakra.git

React Dnd 整体架构图:


dnd-core

核心层主要用来实现拖放

    • 实现了拖放管理器,定义了拖放的交互
    • 和框架无关,你可以基于它结合 react、jquery、RN等技术开发
    • 内部依赖了 redux 来管理状态
    • 实现了 DragDropManager,连接 BackendMonitor
    • 实现了 DragDropMonitor,从 store 获取状态,同时根据store的状态和自定义的状态获取函数来计算最终的状态
    • 实现了 HandlerRegistry 维护所有的 types
    • 定义了 Backend , DropTarget , DragSource 等接口
    • 工厂函数 createDragDropManager 用来接收传入的 backend 来创建一个管理器

react-dnd

上层 React 版本的Drag and Drop的实现

  • 提供 hooks、decorators API.
  • 通过业务层获取 backend 实现和组件来给核心层工厂函数
  • 通过核心层获取状态传递给业务层

backend-html5

  • 主要暴露了一个工厂函数,传入 manager 来获取 HTML5Backend 实例
  • HTML5Backend 实现了 Backend 接口


参考资料:

React DnD 拖放库浅析 https://www.jianshu.com/p/81c1735b1944

The Future of Drag and Drop APIs https://medium.com/@dan_abramov/the-future-of-drag-and-drop-apis-249dfea7a15f

Tags:

最近发表
标签列表