ahooks 是怎么解决 React 的闭包问题的
react 函数式组件闭包导致的问题:
useRef => useLatest
在点击 click me 按钮后 count 无论如何更新,useEffect 回调内 count 获取的始终是 0
function Counter() {
const [count, setCount] = useState(0);
// useEffect 内延时获取的的 count 是一个闭包,要判断闭包的 count 所处于环境也就是
//useEffect 回调执行的
// 时候 count 的值,为了不频繁的创建及销毁定时器,给 useEffect 的依赖是一个空数组
// 正常是在 useEffect 回调内会进行业务逻辑的处理
useEffect(() => {
const id = setInterval(() => {
document.getElementById("p1")!.innerText = `you delay get count: ${count}`;
}, 2000);
return () => {
clearInterval(id);
};
}, []);
return (
<div>
<div> you click {count} times</div>
<div id="p1"></div>
<button onClick={() => setCount(count + 1)}>click me</button>
</div>
);
}
利用 react 自带的 useRef 解决
function Counter() {
const [count, setCount] = useState(0);
//refCount 组件装载后的后续render 后不会再次进行定义,具有唯一性
//可以通过其把最新的值挂载在 useRef 上
const refCount = useRef(0);
refCount.current = count;
// useEffect 内延时获取的的 count 是一个闭包,要判断闭包的 count 所处于环境也就是
// useEffect 回调执行的
// 时候 count 的值,为了不频繁的创建及销毁定时器,给 useEffect 的依赖是一个空数组
// 正常是在 useEffect 回调内会进行业务逻辑的处理
useEffect(() => {
const id = setInterval(() => {
document.getElementById(
"p1"
)!.innerText = `you delay get count: ${refCount.current}`;
}, 2000);
return () => {
clearInterval(id);
};
}, []);
return (
<div>
<div> you click {count} times</div>
<div id="p1"></div>
<button onClick={() => setCount(count + 1)}>click me</button>
</div>
);
}
ahooks 封装的钩子 useLatest
const refCount = useRef(0);
refCount.current = count;
//ahooks useLatest useLatest 源码也是用上面的俩行代码
const refCount = useLatest(count);
useEvent => useMemoizedFn
回掉函数需要缓存,但是 data 形成了闭包 如果把 data 作为依赖项,又会造成缓存失效
const [data, setData] = useState(0);
const callBack = useCallback(() => {
console.log(data);
}, []);
利用 react 自带的 useRef 保持状态数据为最新 如果 useCallback 回调函数里有许多状态数据需要引用,要写好多无用的代码
const [data, setData] = useState(0);
const refData = useRef(data);
refData.current = data;
const callBack = useCallback(() => {
console.log(refData.current);
}, []);
利用 react 自带的 useRef 保持函数为最新,不需要考虑状态数据的问题
const [data, setData] = useState(0);
const fun = () => {
console.log(data);
};
const refData = useRef(fun);
refData.current = fun;
const callBack = useCallback(() => {
refData.current();
}, []);
react 一个新的提案,引入 useEffect 钩子 此函数专门为解决 useCallback 的闭包问题
function useEvent(handler) {
const handlerRef = useRef(null);
// 在组件render 后更新`handlerRef.current`指向
// 这样可以确保 handler 内获取的状态数据都是最新的
useLayoutEffect(() => {
handlerRef.current = handler;
});
// 用useCallback包裹,使得render时返回的函数引用一致
return useCallback((...args) => {
const fn = handlerRef.current;
return fn(...args);
}, []);
}
ahooks 的 useMemoizedFn
function useMemoizedFn<T extends noop>(fn: T) {
// 通过 useRef 保持其引用地址不变,并且值能够保持值最新
const fnRef = useRef<T>(fn);
fnRef.current = useMemo(() => fn, [fn]);
// 通过 useRef 保持其引用地址不变,并且值能够保持值最新
const memoizedFn = useRef<PickFunction<T>>();
if (!memoizedFn.current) {
// 返回的持久化函数,调用该函数的时候,调用原始的函数
memoizedFn.current = function (this, ...args) {
return fnRef.current.apply(this, args);
};
}
return memoizedFn.current as T;
}
react-use
useRequest 网络请求 hooks
网络请求一直是前端应用最为核心的部分之一。从 jQuery 对 ajax 的封装开始到 axios,请求库这几年已经得到了快速的发展。
尤其是随着 hooks 的出现,请求库终于进入了一个新的时代。
在传统的请求模型里,一个请求的完整流程是这样的:
- 用户点击,触发请求流程
- 设置相关视图的状态为 loading
- 发起请求
- 处理响应结果,关闭视图的 loading 状态
最热门的 useRequest、swr 和 react-query 三个请求 hooks
useRequest 参考 SWR 的功能 和 Antd 有更好的配合,react-query 细节控制更好
个人更倾向于 react-query,因为 starts 高,细节做的好
👍🎉🎊