博客:pionpill
官方文档: react.dev/reference/r…
useImperativeHandle 允许我们自定义 forwardRef 中向外暴露的 ref,它的函数声明为:
useImperativeHandle<T, R extends T>(ref: Ref<T>|undefined, init: () => R, deps?: DependencyList): void;
ref:forwardRef的第二个参数init: 返回想暴露的方法的对象deps: 依赖项,和useEffect的依赖项类似
给个官网的例子:
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);
return <input {...props} ref={inputRef} />;
});
然后我们就可以在外部调用这些方法:
export default function Form() {
const ref = useRef(null);
function handleClick() {
ref.current.focus();
}
return (
<form>
<MyInput placeholder="Enter your name" ref={ref} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}
感觉是 useEffect 和 forwardRef 的孩子。
mountImperativeHandle
看源码(✨约2608行):
function mountImperativeHandle<T>(
ref: {current: T | null} | ((inst: T | null) => mixed) | null | void,
create: () => T,
deps: Array<mixed> | void | null,
): void {
// 依赖数组把 ref 也带上
const effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null;
let fiberFlags: Flags = UpdateEffect | LayoutStaticEffect;
mountEffectImpl(
fiberFlags,
HookLayout, // layout 阶段处理
imperativeHandleEffect.bind(null, create, ref),
effectDeps,
);
}
imperativeHandleEffect
function imperativeHandleEffect<T>(
create: () => T,
ref: {current: T | null} | ((inst: T | null) => mixed) | null | void,
): void | (() => void) {
if (typeof ref === 'function') {
// 执行方法
const refCallback = ref;
const inst = create();
refCallback(inst);
// 清除时的回调
return () => {
refCallback(null);
};
} else if (ref !== null && ref !== undefined) {
// 将执行结果挂到 ref.current 上
const refObject = ref;
const inst = create();
refObject.current = inst;
// 清除时的回调
return () => {
refObject.current = null;
};
}
}
updateImperativeHandle
看源码(✨约2642行):
function updateImperativeHandle<T>(
ref: {current: T | null} | ((inst: T | null) => mixed) | null | void,
create: () => T,
deps: Array<mixed> | void | null,
): void {
const effectDeps =
deps !== null && deps !== undefined ? deps.concat([ref]) : null;
updateEffectImpl(
UpdateEffect,
HookLayout,
imperativeHandleEffect.bind(null, create, ref),
effectDeps,
);
}
没啥好说的,一个经 useEffect 封装返回的 ref,文章也许可以放在副作用钩子下?