本文正在参加「 . 」
杭州下雪了,突然想去西湖看雪了,雪后的杭州更有水墨画里的感觉,一下雪杭州就成了临安。希望这个冬天不要太冷吧,也希望来年春暖花开的时候,春天可以真正来到。
闲言少叙,直接进入正文
前言
今天主要想说一下react hooks,react hooks是react v16.8 之后引入的API,现在react都已经到V18了,hooks怎么还能不会用呢?
首先hooks引入的目的是给函数式组件增加数据状态管理的能力,同时增加代码的可复用能力。但是同时hooks也是一个潘多拉魔盒,因为函数式组件不再只是单纯的一个纯函数了,可以在内部处理副作用了,使用不好就会经常遇到各种各样的问题,而且错误的使用方式也会引起re-render,引起一些性能上的问题
本文主要介绍hooks的常见的几个问题与最优实践,同时介绍一下随着React最新版本的API的变化,首先,在使用之前,笔者还是想强调一下
请配置上eslint-plugin-react-hooks
请配置上eslint-plugin-react-hooks
请配置上eslint-plugin-react-hooks
hooks的的使用确实很爽,但是和纯函数相比,还是有挺多反直觉的写法的,比如不能在判断语句中使用hooks。这就很容易有问题,我们需要使用工具来规避这些问题,来提醒我们有些写法是错误的。当然hooks的有其自己的合理性问题,我们暂时不做讨论,这个插件提醒可以保证让我们的写法是符合当前规范的,不至于出现低级错误
异步调用的闭包问题
先看看这段代码,实现一个统计1秒内按钮点击的次数的功能
export default function Demo() {
const [number, setNumber] = React.useState(0);
const click = () =>
setTimeout(() => {
setNumber(number + 1);
}, 1000);
return <button onClick={click}> 点击 {number} 次</button>;
}
当多次点击的时候的时候,显示的点击次数是不对的。点击click方法内的闭包回调函数在组件render的时候捕获了number变量,为了解决这个问题可以使用函数方法来更新数据
const click = () =>
setTimeout(() => {
setNumber(number => number + 1);
}, 1000);
在调用状态更新函数的时候,会将准确的数据回调给当前的更新函数
更近一步
同样的,假如我们统计开屏1秒内的点击次数,在计时结束后,将点击次数发送给server端的时候,就会遇到另一个问题
export default function Demo() {
const [number, setNumber] = React.useState(0);
useEffect(() => {
const timer = setTimeout(() => {
// do fetch
console.log(number);
}, 1000);
return () => {
clearTimeout(timer);
};
// 依赖这里会有个警告
}, []);
return <button onClick={() => setNumber((c) => c + 1)}>点击{number}次</button>;
}
首先我们会遇到一个eslint-plugin-react-hooks
警告
React Hook useEffect has a missing dependency: 'number'. Either include it or remove the dependency array. (react-hooks/exhaustive-deps)
而且执行结果也是由于闭包的原因不能正常提交,熟悉useEffect到都知道,这个原因是使用useEffect的时候,依赖需要添加到依赖数组内,这样才能更新数据到useEffect内,但是在这个例子中,添加了依赖后,每次点击都会引起计时器重新执行,引起倒计时失效,就和需求有冲突了,那这种情况下怎么解决呢?
这个问题其实可以简单归累为:不想让useEffect重新执行的依赖怎么使用的问题,这个场景下就只能使用useRef来实现了
export default function Demo() {
const [number, setNumber] = React.useState(0);
const numberRef = useRef(number);
numberRef.current = number;
useEffect(() => {
const timer = setTimeout(() => {
// do fetch
console.log(numberRef.current);
}, 1000);
return () => {
clearTimeout(timer);
};
}, []);
return (
<button onClick={() => setNumber((c) => c + 1)}>点击{number}次</button>
);
}
通过useRef可以来保证访问到的number一直是最新的,解决了闭包的问题,同时useEffect未直接依赖number,当number变化的时候不会引起重新执行
小结
虽然和hooks的写法有关,本质上还是闭包问题,比如forEach内写的定时器也会有同样的问题;但是对于异步场景下的写法,不想引起useEffect重新执行的变量,就可以用useRef做一层代理,这种用法只能说是对react hooks设计的一种妥协
用useCallback肯定能提升性能吗
useCallback的作用是缓存函数,避免重复生成新函数,引起组件重新渲染,比如:
const Children = (props) => <button onClick={props.doFetch}>提交</button>;
export default function Demo() {
const doFetch = useCallback(() => {
// fetch();
}, []);
return <Children doFetch={doFetch} />;
}
这是一个很常见的写法,但是需要明确在class组件的时候我们通常使用shouldComponentUpdate
来拦截更新,通过比较父组件传入的props的变化,来判断是否re-render子组件,上面的
声明:本文仅供个人学习使用,来源于互联网,本文有改动,本文遵循[BY-NC-SA]协议, 如有侵犯您的权益,请联系本站,本站将在第一时间删除。谢谢你