一. useImperativeHandle
方法介绍
useImperativeHandle
方法接收三个参数,第一个参数是ref
对象,第二个参数是create
方法,会调用create
方法将返回值赋值给ref.current
属性,第三个参数是依赖deps
,当依赖deps
发生变化时会重新调用create
获取新的返回值赋值给ref.current
例如下面这段代码,通过useImperativeHandle
方法将setCount
和getCount
方法赋值给ref.current
,提供给外部组件调用
function HelloWorld({ ref }) {
const [count, setCount] = useState(0)
useImperativeHandle(
ref,
() => ({
setCount(value: number) {
setCount(value)
},
getCount() {
return count
},
}),
[count],
)
return <h1>Hello World</h1>
}
function App() {
const ref = useRef()
return <HelloWorld ref={ref} />
}
二. 实现useImperativeHandle
useImperativeHandle
内部实现依赖useLayoutEffect
,可以参考文章手写React useLayoutEffect,理解useLayoutEffect原理
2.1 首次调用useImperativeHandle
方法
在更新DOM
阶段,会执行imperativeHandleEffect
方法,调用create
方法获取返回值赋值给ref.current
function imperativeHandleEffect(ref, create) {
ref.current = create()
return () => {
ref.current = null
}
}
function mountImperativeHandle(ref, create, deps) {
// 创建Layout类型的Effect对象
mountLayoutEffect(imperativeHandleEffect.bind(null, ref, create), deps)
}
2.2 更新调用useImperatvieHandle
方法
在更新DOM
阶段,会先执行imperativeHandleEffect
方法返回的destroy
方法,然后再调用create
方法获取返回值赋值给ref.current
function imperativeHandleEffect(ref, create) {
ref.current = create()
return () => {
ref.current = null
}
}
function updateImperativeHandle(ref, create, deps) {
// 创建Layout类型的Effect对象
updateLayoutEffect(imperativeHandleEffect.bind(null, ref, create), deps)
}
2.3 定义useImperativeHandle
方法
如果新节点不存在旧FiberNode
节点,说明是首次调用函数组件方法,则调用mountImperativeHandle
方法,否则调用updateImperativeHandle
方法
function useImperativeHandle(ref, create, deps = null) {
const current = currentlyRenderingFiber.alternate
if (current === null) {
mountImperativeHandle(ref, create, deps)
} else {
updateImperativeHandle(ref, create, deps)
}
}