邮箱收件人组件成长历程(二)(React hooks升级版)
邮箱收件人组件成长历程(二)(React hooks升级版)
记得自己之前写过一篇《邮箱收件人组件(vue版)成长历程(一)》记得当时里面写到了自己使用的是可编辑div来进行输入的,同时提到当时出于挑战自己和青铜的倔强,想试着换个方案,完全使用可编辑div来实现。。。这个小小的倔强为后续很多功能瓶颈埋下了隐患。。。
使用contenteditable的div缺点
具体的隐患是什么的?
- 因为方案中采用的点击插入新收件人时实际上就是向数组里面插入一个空收件人对象,为了确保用户在胡乱点击时永远保证只有一个空对象,就需要频繁的做是否有多余空对象的检查。这个会导致数组频繁的重新渲染,鼠标点入进行选中元素变得很困难。
- 普通用户、产品和设计师一般会认为这个收件人组件是一个输入框(你见过谁家的输入框里面能展示校验成功与否的图标的),这会导致他们的需求和改版是以input的某些特性来设计的,这会部分场景用contenteditable的div无法实现,比如用户聚焦后改变整个组件背景颜色。
这次赶上项目迁移至React,对这部分也做了设计上的调整,改用主流的input方法来做输入和编辑态。
响应点击部分还是跟之前一样
代码结构
- 全局只有一个input,解决上述频繁去空检查的问题,将input的输入内容和数组中的某个index进行关联,该index位置就是input内容后续的插入位置,index后面的内容自然向后移动一位。需要编辑或插入内容时,移动input至对应的index。
- 在整个组件内但是非已存在收件人信息以外的区域(上图中的蓝框区域)点击时,认为是在最后面添加新的收件人,让input直接移动在列表的最后面,它的输入值会关联到当前收件人数组list的最后一个,关联index为list.length。
- 点击到某个收件人预览态(图中的绿框)时将其高亮,如果是双击的化则将其置为编辑态(将input移动到当前收件人位置,并将其数据从list中删除,关联input与该收件人index,待输入完成后再插回此index位置);如果是单击它前面(第一个小箭头所指区域),则是将input移动到它前面,关联input与该收件人index,带输入完成后,将新增内容插入该index。
代码片段:
在变更list数据后React会重新渲染,根据上述代码结构,input会重新出现在列表的最后面,我们需要待渲染完毕后通过图中的ref移动input到它需要出现的位置。
// 回车上屏插入数据
function onInputEnterInsert(data) {
const result = getAutoCompleteResult(); // 当前是否选中了搜索候选词
const finalData = result.value ? result : data; // 如果有候选信息,直接插入候选信息,无视输入信息
insertData(finalData);
afterDataInserted(state.inputIndex);
}
插入数据过程
// 在列表中插入数据
function insertData(data) {
const dataInfo = {
...data,
_addedTime: Date.now(), // 方便作为key
};
if (isLastIndexOfList(state.inputIndex)) { // 增加新的
list.push(dataInfo);
} else { // 双击后更新的
list.splice(state.inputIndex, 0, dataInfo);
}
props.onChange(list.slice()); // 变更state触发渲染
clearAddress();
}
渲染后调整input位置
// 在当前inputIndex插入数据后的处理流程
function afterDataInserted(indexBeforeInsert) {
console.log('sis hook after inserted -> ', indexBeforeInsert, list[indexBeforeInsert], list);
typeof afterEditHook === 'function' && afterEditHook(indexBeforeInsert, list[indexBeforeInsert], list);
// 确保数据变更+input显示后再处理
setTimeout(() => { // 此时重新渲染后输入框已在最后面
if (!isLastIndexOfList(indexBeforeInsert)) { // 只要不是在最后面插入的,都需要将列表render后的置于最后面的input移动至当前上次锚定节点的前面
const targetNode = containerRef.current.children[indexBeforeInsert + 1]; // list里面已经新插入了一个,故原锚定节点的index+1
containerRef.current.insertBefore(inputContainerRef.current.node, targetNode);
// dom调整后修正index和input的指向
dispatch({
inputIndex: indexBeforeInsert + 1,
});
}
doInputFocus(0);
}, 0);
}
可以看到上面还加入了部分hook,方便用户在对应周期处理其它逻辑。 之前的Vue版本在产品迭代过程加入了一些新功能
- 列表数量上限
- 候选列表只有一个时自动选中
- 收件人直接拖动以及跨栏拖动
这部分功能在本次React版本也会同步支持,后面单独写一篇讲一下。
- 分类:
- Web前端
相关文章
使用next.js服务端渲染经历
上周末的时候打算把自己的网站从vue的ssr转换为react的ssr,鉴于之前在vue中是用的原生的ssr,这次想在react中试试框架,所以首选的就是next.js。 第一次用next.js,根据 阅读更多…
Did you mean to use React.forwardRef()?搞懂react的createRef和forwardRef
最近在使用react过程中发现在使用ref时的一些场景,自己初步感觉react的ref没有vue那么强大。 现在我就简单看下怎么使用ref? createRef 我们直接看源码 / 阅读更多…
Vue和React hooks实现Select批量选择、选中条数、全选、反选实现对比
批量选择、全选、反选这些功能使用频率还是很高的,下面直接看看Vue和React分别怎么实现吧。 Vue 在使用Vue的时候是很容易实现的,我们以下列数据格式为例: const data 阅读更多…
2021年的一点工作总结(一)迁移React技术栈
2021年全年的工作总结起来很简单,算是做苦力的一年吧。。。 2021年春节后就开始邮件项目从Vue迁移到React的工作以及富文本编辑器由wangEditor替换成CKEditor。 其实自己 阅读更多…
React执行调度流程梳理笔记
触发更新 我们最常见的触发更新的方式就是更新state了,可以分别从类组件和函数组件看看这时会发生什么。 类组件之 setState: 当触发 setState 本质上是调用 enque 阅读更多…
怎么调试Webpack+React项目,报错basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")怎么办
今天在WebStorm上Windows上准备调试一个React项目,就出现了这样的报错。 Node Parameters里面写的是webpack-dev-server的执行文件 .\node_mod 阅读更多…