return (
{children}
);
};
export default CopyButton;
整体的代码是比较简洁的,可以在以下沙盒中试用:
https://codesandbox.io/s/copy-button-o541i?file=/src/CopyButton.tsx:0-703codesandbox.iocopy-button - CodeSandboxCodeSandbox - Copy Buttoncodesandbox.io
分解
–
作为一个简单的组件,它的逻辑并没有什么突出的复杂度,其中比较关键的是如何让出现的“复制成功”的提示信息可以在一段时间后自动消失。
正常情况下,我们会选择使用一个状态来控制提示是否出现:
const [visible, setVisible] = useState(false);
const show = useCallback(
() => setVisible(true),
[]
);
而如果我们需要让它在一定时间后自动消失的话,就势必要在值改变的时候,打开一个定时器,设定指定的时间后将值撤销。我们也知道,凡是遇到定时器的场合,我们就要处理好多次打开定时器之间的竞争关系。
对于这样的场景,有2种解法,第一种是在值变更的时候,命令式地打开定时器。但这时你就需要管理好定时器的标记,记得把前一次的定时给关掉:
const timer = useRef(-1);
const show = useCallback(
() => {
clearTimeout(timer.current);
setVisible(true);
timer.current = setTimeout(
() => setVisible(false),
delay
);
},
[delay]
);
切记一点,定时器标记这样的值,它在组件的渲染过程中是不需要的,所以不需要使用一个state去管理,用useRef能保持住值就行。
上面的代码其实有一些瑕疵,当组件销毁后,定时器依然可能执行,调用一次setVisible,此时在开发模式下会产生被控制台里的一个警告,但不会有什么负面效果。
而另一个办法,是使用useEffect来观察值的变化并管理定时器:
useEffect(
() => {
if (visible) {
const tick = setTimeout(
() => setVisible(false),
delay
);
return () => clearTimeout(tick);
}
},
[delay, visible]
);
useEffect带来的“副作用 - 取消副作用”的方式,可以很方便地管理定时器,也不会产生组件销毁后定时器仍然执行的情况,从复杂度上来说,我们更愿意选择这样的方案。
当然上面的代码依然存在一些瑕疵,当delay(也许是从props中来的)变化时,定时器会被取消并生成一次新的定时,但这往往并不是我们想要的效果,因为功能面向用户,用户只需要在点击按钮出现提示后,提示按照预期的时间自动消失。
那如果我们不把delay作为useEffect的一个依赖传递呢?虽然在行为是完全符合预期,却会让eslint报一个错,非常不适合强迫症,也可能导致delay真正发生变化后,用户点击出现的消息并不按最后的delay时间消失。
所以在这里,我们就要启用useRef的“作弊模式”。eslint的规则会判断一个值是否为ref,并识别其不需要加入到useEffect、useCallback等的依赖中。当一个值并不会影响渲染,也不需要引发副作用时,使用useRef去托管就是一个很好的选择。
const delayRef = useRef(delay);
useEffect(() => {
delayRef.current = delay;
}, [delay]);
useEffect(() => {
if (visible) {
const tick = setTimeout(() => {
setVisible(false);
}, delayRef.current);
return () => clearTimeout(tick);
}
}, [visible]);
而把这些逻辑串起来,形成“一个变化后会自动变回去的状态”这样的概念,额外再抽象一些能力,比如:
可以是什么类型,不局限于boolean,并可以指定初始值。 可以设定默认的持续时间。 可以在每一次修改状态时,指定一个临时的持续时间。 允许在持续过程中手动设置回默认值。
总结
–
从一个简单的复制按钮的交互开始,在这一篇中重点讲解了如何使用状态+定时器的组合来实现一个过渡式的状态,并让状态自动返回初始值,其中的要点有:
与渲染无关的数据可以使用useRef存储,不需要useState管理状态。 可以使用命令式或useEffect的方式管理定时器,但往往使用useEffect更为方便,也能照顾到组件销毁时的情况。 对于不希望引发useEffect的数据,可以使用useRef管理形成一种“作弊”骗过eslint,同时确保能在useEffect的闭包中取到最新的值。
这个hook可用在所有临时出现的场景,包括提示信息、消息气泡等,一定程度上配合CSS的动画能取得更好的效果。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(资料价值较高,非无偿)
前端面试题汇总
前端面试题是我面试过程中遇到的面试题,每一次面试后我都会复盘总结。我做了一个整理,并且在技术博客找到了专业的解答,大家可以参考下:
由于篇幅有限,只能分享部分面试题,完整版面试题及答案可以【点击我】阅读下载哦~
感悟
ert/0432b8e539325b94948089383d0bfa8c.png)
由于篇幅有限,只能分享部分面试题,完整版面试题及答案可以【点击我】阅读下载哦~
感悟
春招面试的后期,运气和实力都很重要,自己也是运气比较好,为了回馈粉丝朋友们(毕竟自己也玩了这么久哈哈哈),整理个人感悟和总结以上。最后祝愿大家能够收获理想offer!!
文章来源
发表评论