看react-use hooks库和ahooks之后的一些收获
1
1272086709@163.com
2026年5月23日66 次阅读0 条评论
关于 useRef 和 useState 传递函数作为初始值
- 在使用
useState时,如果传递一个函数,则该函数获取对应的初始值只会执行一次。 useRef可以用来存储函数,这样子函数的地址就不会发生改变。
下面示例展示了引用地址的变化与没有变化:
import { useState, useRef } from 'react';
import { useClickAway } from 'ahooks';
class A extends Object {
constructor() {
super();
console.log('A...constructor');
}
}
const map = new Map();
function logMap(ref: any, category: string) {
const aRefCnt = map.get(ref);
if ([null, undefined].includes(aRefCnt)) {
map.set(ref, 0);
} else {
map.set(ref, parseInt(aRefCnt) + 1);
}
console.log('cate:', category, 'map', aRefCnt);
}
export default () => {
const [counter, setCounter] = useState(0);
const ref = useRef<HTMLButtonElement>(null);
// 使用 useRef 传递一个函数,函数会在初始化时执行一次
const aRef = useRef((name: string) => {
console.log('name', name);
new A();
});
// 这里传递的是一个对象实例,因此当 state 变化时,A 的构造函数每次都会重新执行
const bRef = useRef(new A());
console.log('bRef:', bRef.current);
const cnt = useState(0)[0];
// 可以看出,useState 解构出来的函数,当 state 发生变化后,它的地址也是固定的
logMap(setCounter, 'setCounter');
logMap(cnt, 'cnt');
logMap(bRef.current, 'bRef.current');
logMap(aRef.current, 'aRef.current');
useClickAway(() => {
// logMap(aRef.current);
setCounter((s) => s + 1);
}, ref);
return (
<div>
<button ref={ref} type="button">
box
</button>
<p>counter: {counter}</p>
</div>
);
};
获取 query 参数的方法
下面示例可以用于获取 URL 中的某些参数:
const getValue = (search: string, param: string) => new URLSearchParams(search).get(param);
关于闭包陷阱问题
- 存在闭包陷阱的 Hooks 包括:
useCallback,useEffect,useMemo等,只要依赖元素为空数组,就可能存在闭包陷阱。
下面例子展示了:
window.location.hash并不会取原始值,而是会取最新的值。因为它是一个全局变量,在 React 组件外部定义的:
const onHashChange = useCallback(() => {
setHash(window.location.hash);
}, []);
useLifecycles(
() => {
on(window, 'hashchange', onHashChange);
},
() => {
off(window, 'hashchange', onHashChange);
}
);
- 存在闭包陷阱的例子:
const [count, setCount] = useState(0);
const [info, setInfo] = useState({
age: 0,
sex: '男',
});
const setObj = useCallback(() => {
console.log('info', info);
console.log('count', count);
console.log('global:', globalUserInfo.name);
}, []);
useSafeState(来自 ahooks)
类似于
useState,区别在于它可以防止内存泄漏。
主要场景:在一个组件开始挂载时,会执行一些副作用的异步操作,并设置 state,但这个组件可能存在卸载的情况。当组件卸载时,调用
setState是没有意义的,会导致内存泄漏。
import React, { useState } from 'react';
import useSafeState from '../index';
const Child = () => {
const [value, setValue] = useSafeState<string>();
React.useEffect(() => {
setTimeout(() => {
setValue('data loaded from server');
}, 5000);
}, []);
const text = value || 'Loading...';
return (
<div>{text}</div>
);
};
export default () => {
const [visible, setVisible] = useSafeState(true);
return (
<div>
<button onClick={() => setVisible(false)}>Unmount</button>
{visible && <Child />}
</div>
);
};
getBoundingClientRect() 与 offsetWidth/offsetHeight 的区别
getBoundingClientRect()方法返回的width、height,是受transform: scale、scaleX、scaleY影响的,而offsetWidth和offsetHeight却不会。
- 相同点:
这两者都包含
padding、border,实际的宽度和高度,但不包含border。
下面代码可以用于判断当前节点与父节点之间的真实距离:
export const getRectDiff = (node: HTMLElement, parentNode: HTMLElement) => {
const nodeRect = node.getBoundingClientRect();
const parentRect = parentNode.getBoundingClientRect();
const scaleX = parentNode.offsetWidth / parentRect.width;
const scaleY = parentNode.offsetHeight / parentRect.height;
return {
left: (nodeRect.left - parentRect.left) * scaleX,
top: (nodeRect.top - parentRect.top) * scaleY,
right: (nodeRect.right - parentRect.right) * scaleX,
bottom: (nodeRect.bottom - parentRect.bottom) * scaleY,
};
};
import { useEffect, useRef } from 'react';
// 测试一下
function Demo11() {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const { width, height } = ref.current?.getBoundingClientRect() || {};
// 打印出来 210, 210, 会显示真实的宽度和高度
console.log('width', width);
console.log('height', height);
// 不能显示出来真实的宽度和高度,显示原来的宽度和高度
// 通过 offset 的方式
console.log('offsetWidth', ref.current?.offsetWidth);
console.log('offsetHeight', ref.current?.offsetHeight);
}, []);
return (
<div>
<h2>测试一下 scaleX, scaleY 对一些 JS 的影响</h2>
<div ref={ref} style={{
width: '300px',
height: '300px',
border: '1px solid red',
padding: '10px',
transform: 'scale(0.7)',
overflowX: 'auto',
overflowY: 'auto',
}}>
<p style={{
// whiteSpace: 'nowrap',
maxWidth: '100%',
}}>开心每一天开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天,
开心每一天开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天,
开心每一天开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天,
开心每一天开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天,
开心每一天开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天,
开心每一天开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天,
开心每一天开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天,
开心每一天开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天, 开心每一天开心每一天,
</p>
</div>
</div>
);
}
export default Demo11;
加载评论中...