写React Hooks前需要注意什么?,客户端开发面试题目

}, [count]);

那这样肯定就不对了,相当于每次count变化,都会重新绑定一次事件。所以对于事件的绑定,或者类似的场景,有几种思路,我按我的常规处理优先级排列:

思路1:消除依赖

在这个场景里,很简单,我们主要利用 setCount 的另一个用法 functional updates。这样写就好了:

() => setCount(prevCount => ++prevCount) ,不用关心什么新的旧的、什么闭包,省心省事。

思路2:重新绑定事件

那如果我们这个事件就是要消费这个count怎么办?比如这样:

dom.current.addEventListener(‘click’, () => {

console.log(count);

setCount(prevCount => ++prevCount);

});

我们不必执着于一定只在mount时执行一次。也可以每次重新render前移除事件,render后绑定事件即可。这里利用useEffect的特性,具体可以自己看文档:

useEffect(() => {

const $dom = dom.current;

const event = () => {

console.log(count);

setCount(prev => ++prev);

};

$dom.addEventListener(‘click’, event);

return () => $dom.removeEventListener(‘click’, event);

}, [count]);

思路3:如果嫌这样开销大,或者编写麻烦,也可以用 useRef

其实用 useRef 也挺麻烦的,我个人不太喜欢这样操作,但也能解决问题,代码如下:

const [count, setCount] = useState(0);

const countRef = useRef(count);

useEffect(() => {

dom.current.addEventListener(‘click’, () => {

console.log(countRef.current);

setCount(prevCount => {

const newCount = ++prevCount;

countRef.current = newCount;

return newCount;

});

});

}, []);

useCallback与useMemo

这两个api,其实概念上还是很好理解的,一个是「缓存函数」, 一个是缓存「函数的返回值」。但我们经常会懒得用,甚至有的时候会用错。

从上面依赖问题我们其实可以知道,hooks对「有没有变化」这个点其实很敏感。如果一个effect内部使用了某数据或者方法。若我们依赖项不加上它,那很容易由于闭包问题,导致数据或方法,都不是我们理想中的那个它。如果我们加上它,很可能又会由于他们的变动,导致effect疯狂的执行。真实开发的话,大家应该会经常遇到这种问题。

所以,在此建议:

  1. 在组件内部,那些会成为其他useEffect依赖项的方法,建议用 useCallback 包裹,或者直接编写在引用它的useEffect中。

  2. 己所不欲勿施于人,如果你的function会作为props传递给子组件,请一定要使用 useCallback 包裹,对于子组件来说,如果每次render都会导致你传递的函数发生变化,可能会对它造成非常大的困扰。同时也不利于react做渲染优化。

不过还有一种场景,大家很容易忽视,而且还很容易将useCallback与useMemo混淆,典型场景就是:节流防抖。

举个例子:

function BadDemo() {

const [count, setCount] = useState(1);

const handleClick = debounce(() => {

setCount(c => ++c);

}, 1000);

return 

{count}
;

}

我们希望防止用户连续点击触发多次变更,加了防抖,停止点击1秒后才触发 count + 1 ,这个组件在理想逻辑下是OK的。但现实是骨感的,我们的页面组件非常多,这个 BadDemo可能由于父级什么操作就重新render了。现在假使我们页面每500毫秒会重新render一次,那么就是这样:

function BadDemo() {

const [count, setCount] = useState(1);

const [, setRerender] = useState(false);

const handleClick = debounce(() => {

setCount(c => ++c);

}, 1000);

useEffect(() => {

// 每500ms,组件重新render

window.setInterval(() => {

setRerender(r => !r);

}, 500);

}, []);

return 

{count}
;

}

每次render导致handleClick其实是不同的函数,那么这个防抖自然而然就失效了。这样的情况对于一些防重点要求特别高的场景,是有着较大的线上风险的。

那怎么办呢?自然是想加上 useCallback :

const handleClick = useCallback(debounce(() => {

setCount(c => ++c);

}, 1000), []);

现在我们发现效果满足我们期望了,但这背后还藏着一个惊天大坑。

假如说,这个防抖的函数有一些依赖呢?比如 setCount(c => ++c); 变成了 setCount(count + 1) 。那这个函数就依赖了 count 。代码就变成了这样:

const handleClick = useCallback(

debounce(() => {

setCount(count + 1);

}, 1000),

[]

);

大家会发现,你的lint规则,竟然不会要求你把 count 作为依赖项,填充到deps数组中去。这进而导致了最初的那个问题,只有第一次点击会count++。这是为什么呢?

因为传入useCallback的是一段执行语句,而不是一个函数声明。只是说它执行以后返回的新函数,我们将其作为了 useCallback 函数的入参,而这个新函数具体是个啥,其实lint规则也不知道。

更合理的姿势应该是使用 useMemo :

const handleClick = useMemo(

() => debounce(() => {

setCount(count + 1);

}, 1000),

[count]

);

这样保证每当 count 发生变化时,会返回一个新的加了防抖功能的新函数。

总而言之,对于使用高阶函数的场景,建议一律使用 useMemo

有些网友提供了宝贵的反馈,我继续补充:刚使用useMemo,依旧存在一些问题。

问题1useMemo「将来」并不「稳定」

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

深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img
img
img
img

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

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
img

刷面试题

刷题的重要性,不用多说。对于应届生或工作年限不长的人来说,刷面试题一方面能够尽可能地快速自己对某个技术点的理解,另一方面在面试时,有一定几率被问到相同或相似题,另外或多或少也能够为自己面试增加一些自信心,可见适当的刷题是很有必要的。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 前端字节跳动真题解析

  • 【269页】前端大厂面试题宝典

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 前端字节跳动真题解析

  • 【269页】前端大厂面试题宝典

最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。大厂面试远没有我们想的那么困难,摆好心态,做好准备,你也可以的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值