从业务案例来讲 React Hook 系列 - 一个复制按钮

const CopyButton: FC = ({text, children}) => {

const [noticing, setNoticing] = useTransitionState(false, 2500);

const copy = useCallback(() => setNoticing(true), [setNoticing]);

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]

);

而把这些逻辑串起来,形成“一个变化后会自动变回去的状态”这样的概念,额外再抽象一些能力,比如:

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!**

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值