- Published on
- 约 1366 字
一次React DnD使用失效带来的几点思考
- Authors
- Name
- 小辉辉
背景
眼看着离上次发文又过去了一个月了,这文章可是一篇都没有,在这一个月里倒不是说没有题材可写,主要是这个月实在累的不行,连续一个月单休了,也就只有周末一天休息时间,白天又要负责做面包。
除此之外还有一个很难的开发任务,就是优化Antd表格性能,幸好最新的antd table版本支持了虚拟列表,但是在升级这一块也遇到了很多问题,有样式的,有功能失效的,为解决这些问题真的耗费了我不少心神。不过还好,最后算是基本升级成功了,之后也看看有没有时间记录下这次升级改造经历。
现在来说正经事了,也就是昨天加班,有一同事和我说他那个React DnD拖拽功能就是有问题,弄了很久了也没有弄出来,连最基本的拖动都没有实现出来,我想着这这拖动框架我们项目内部已经大规模使用,应该不会有问题啊,所以就想着看看是不是用法不对了,上去一看用法,基本是没有什么大问题,主要就是useDrop
和useDrag
两个钩子函数,入参没有啥问题,但是实际的页面在拖动的时候就是没有效果,为了查看调试结果,也打出了日志,有个isDragging
状态也一直提示为false,这就明摆着拖动功能没有实现啊,但是也一下子找不到是哪个参数错误了,控制台也没有相关的错误提示。
实在没有办法了,我和同事说你这个问题先放一放,我在自己的项目里试试,没几分钟我就弄完了,最后的结果也在我意料之中,拖动正常,没有任何问题,这就奇怪了,同事项目里的写法咋就有问题呢?
问他要了他的组件代码,显示看了下上面两个hook函数的入参,没有问题,最后决定在仔细分析下代码,最后终于发现了异常,下面给出相关错误代码:
import {useDrag, use Drop} from 'react-dnd';
const DragTest = () => {
const c = ({item}) => {
const [, drag] = useDrag(() => ({
type: 'dragTest',
item,
}));
return (
<div ref={drag}>
{item}
</div>
)
}
const [, drop] = useDrop(()=> ({
accept: 'dragTest';
}));
return (
<div ref={drop}>
{[1,2,3].map((item)=>{
return (
<DragItem item={item} key={item} />
)
})}
</div>
)
}
各位老师们,发现问题了吗,问题就出在drag的组件DragItem上面,这个组件本身的实现没有问题,问题在定义的位置不对,它是定义在DragTest组件内部的,有没有感觉到很奇怪?我们都不知道React组件的定义很随意,放在哪里都可以,这也给我们开发带来了很大的灵活性,但是一般来说我们的组件都是放在一个独立文件里面,由引入的形式来使用,但是为什么这里定义在另一个组件内部的组件和React DnD配合使用起来就有问题呢?
为了验证我们的猜测,将上述代码中DragItem组件的定义移动到了外部,再查看效果,拖动功能正常。
分析
说到这里,我们先进行一个反推假设失效的原因,第一步开始拖动,拖动过程因为要实时输出当前组件isDragging
的状态,所以必定会触发DragTest的重新渲染,由此也会带来下面DragItem的重新渲染,这样才能记录isDragging的状态,那这次的重新渲染有什么不同呢?DragTest组件的内部在重新渲染的时候生成了一个新的DragItem组件,那随着而来的就是DragItem以这个新组件进行重新渲染了,由于是首次加载,那必然输出了isDragging为false的结果,那上次的那个DragItem组件呢?自然是在重渲染前被销毁掉了。
好了,最后来缕一缕这个过程:
- 首次加载DragItem组件,使用的是A状态下的DragItem
- 开始拖动
- 拖动带来DragTest组件重新渲染
- 此时原有A状态的DragItem被重新定义为了B状态下的DragItem
- DragTest组件重新渲染带来DragItem组件重新渲染,此时引用为B状态对应的组件
- React检测到两次组件类型不一致,进行内部的协调更新机制
- 先销毁原有A状态的DragItem组件
- 再重新创建B状态的DragItem组件
- 这样一来导致在拖动过程中A状态下组件的状态丢失,都会被还原到B状态组件下的初始化状态
总结
受到这个问题的启发,后面也有必要记录下React内部组件更新的机制,这里就不一一展开了,唯一要说的是我们在开发过程中还是要注意相关开发规范,这也是为什么我们需要用eslint
来避免一些常见的不推荐写法,因为这些写法很有可能让我们走入一些误区,而这些误区如果不仔细排查很难被发现,导致最后开发效率受到影响。