关于useRef和useState 传递函数作为初始值
- useState 如果传递一个函数,则这个函数获取对应的初始值只会执行一次。
- useRef 可以用来存储函数,这样子函数地址可以不发生改变。
如下所示DEMO,说明引用地址的变化与没有变化
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, useEfffect, 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,scale对一些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;