一些 hooks 收集
useReducer 的优雅之处
基于 ref 数据的 state 更新
const [bookList, updateBookList] = useReducer(() => {
return bookPageInfoRef.current.list; // 更新的都是 ref 数据
}, []);
zustand 的 force rerender 的实现:
const [, forceUpdate] = useReducer((c) => c + 1, 0) as [never, () => void];
useCallbackRef
from myself,作用是对于需要用 ref 的 callback 做了一层语法糖包裹
其实再猛一点可以直接把 deps 包进去,里面用 useMemo/useCallback
但为了能够让非 ref 的 callback 依然能正常使用,就还是留了一手
import React, { useLayoutEffect, useRef } from "react";
export const useCallbackRef = <T = (args: any) => void>(callback: T) => {
const callbackRef = useRef(callback);
useLayoutEffect(() => {
callbackRef.current = callback;
}, [callback]);
return callbackRef;
};
Usage
const foo = useCallback(() => {
// ...
}, [a, b, c]);
const fooRef = useCallbackRef(foo);
useAsyncState
来自杨大师的题目
useAsyncState<T>(initState: T) => [T, (nextState: PromiseLike<T> | T) => void];
// 三个细节是: // 1、传同步的时候不要额外等待(禁用掉 Promise.then(),你必须分类讨论) // 2、数据同步必须安全,过时的异步任务不能更新状态 // 3、注意组件卸载后 setState 会报错
// 还有加分项: // 1、参数控制异常处理,产生异常后状态回退或重置或返回新的 state 标记是否有 error【并没有处理异常】 // 2、标记当前是否在 loading(参数控制时可能需要清除当前状态) // 3、加上 useState 接收回调的机制,再考虑回调是否要等待未完成的 Promise(不难但是代码会很长很恶心)【不知道实现的对不对】
import { useCallback, useEffect, useRef, useState } from "react";
export const useAsyncState = <T>(
initState: T
): [T, (nextState: PromiseLike<T> | T) => void, boolean] => {
const [{ state: inner, waiting }, setInner] = useState({
state: initState,
waiting: false,
});
const currentRenderId = useRef(0);
!waiting && (currentRenderId.current += 1);
const mounted = useRef(true);
useEffect(() => {
return () => {
mounted.current = false;
};
}, []);
const safeSet = useCallback((v: T, id?: number) => {
if (
mounted.current &&
(id === undefined || currentRenderId.current === id)
) {
setInner({
state: v,
waiting: false,
});
}
}, []);
const set = useCallback(
(nextState: PromiseLike<T> | T | ((prev: T) => PromiseLike<T> | T)) => {
const renderId = currentRenderId.current;
const next =
typeof nextState === "function"
? (nextState as (prev: T) => PromiseLike<T> | T)(inner)
: nextState;
if (typeof (next as Promise<T>).then === "function") {
setInner((v) => ({
...v,
waiting: true,
}));
(next as Promise<T>)
.then((res) => {
safeSet(res, renderId);
})
.catch?.((err) => {});
} else {
safeSet(next as T);
}
},
[]
);
return [inner, set, waiting];
};