邮箱收件人组件成长历程(二)(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前端
相关文章
React执行调度流程梳理笔记
触发更新 我们最常见的触发更新的方式就是更新state了,可以分别从类组件和函数组件看看这时会发生什么。 类组件之 setState: 当触发 setState 本质上是调用 enque 阅读更多…
React router用hooks读取routeName、根据routeName跳转
在迁移Vue至React的过程中遇到了一些路由相关的问题,在Vue项目中经常会使用routeName,毕竟使用path太长了,也记不住,我自己看了看React router也没有发现routeNam 阅读更多…
项目Vue转成React hooks可能存在的问题--急需类似setState回调
假设在Vue中有如下三个方法,并且初始时 this.a = ‘a’; this.b = ‘b’; funA() { this.a = '1221 阅读更多…
Vue和React hooks实现Select批量选择、选中条数、全选、反选实现对比
批量选择、全选、反选这些功能使用频率还是很高的,下面直接看看Vue和React分别怎么实现吧。 Vue 在使用Vue的时候是很容易实现的,我们以下列数据格式为例: const data 阅读更多…
邮箱收件人组件(vue版)成长历程(一)
前期项目中需要优化原始的收件人、抄送、密送部分,换成更加现代化的样式和用户,当时将这部分抽象成一个组件了,最近的需求是发件人也要使用该组件,鉴于发件人比收件人等需要校验的地方和交互习惯变动点较多,进 阅读更多…
对React Hooks的Capture value特性的理解
之前我的项目里面很多功能都是用的事件驱动,所以下面的实例也会更多地使用监听事件的回调函数。 我们先看下测试代码 const {useEffect ,useState, useRef, us 阅读更多…