源码分析之React中的useImperativeHandle

概述

useImperativeHandle用子组件安全地向父组件提供访问方法或者变量的句柄。通常和React.forwardRef配合使用。

源码分析

useImperactiveHandle同其他hooks 一样,在挂载和更新阶段是对应不同的方法,分别为:mountImperativeHandleupdateImperativeHandle

源码解读

  • mountImperativeHandle方法
js 复制代码
function mountImperativeHandle(ref, create, deps) {
  const effectDeps =
    deps !== null && deps !== undefined ? deps.concat([ref]) : null;

  let fiberFlags = UpdateEffect | Layout;

  mountEffectImpl(
    fiberFlags,
    HookLayout,
    imperativeHandleEffect.bind(null, create, ref),
    effectDeps,
  );
}
  • updateImperativeHandle方法
js 复制代码
function updateImperativeHandle(ref, create, deps) {
  const effectDeps =
    deps !== null && deps !== undefined ? deps.concat([ref]) : null;

  updateEffectImpl(
    UpdateEffect,
    HookLayout,
    imperativeHandleEffect.bind(null, create, ref),
    effectDeps,
  );
}

可以明显看出,上面两个方法和effect的挂载更新几乎如出一辙,都是调用mountEffectImplupdateEffectImpl进行处理,而且useImperativeHandleuseLayoutEffect使用的tag标记是一样的,因为useImperativeHandle的本质给ref参数赋值,而useLayoutEffect这个hook是在DOM挂载后、DOM绘制前调用,tag一致可以保证useImperativeHandle的调用时机也是这个时机。

另一方面,useRef这个hook也可以用于绑定DOM,这也遵守了相同规则。

useLayoutEffect不同的是,useImperativeHandle会自动的将ref也作为依赖的一部分,并且通过bind方法对create回调方法进行了封装。当依赖发生变化时,便会调用imperativeHandleEffect方法,在其内对ref进行赋值。

其源码如下:

  • imperativeHandleEffect方法
js 复制代码
function imperativeHandleEffect(create, ref) {
  // 判断ref是否是函数
  if (typeof ref === 'function') {
   // 若ref是函数
    const refCallback = ref;
    // 先执行create方法 
    const inst = create();
    // 然后将create方法的返回值作为ref的参数,执行ref函数
    const refCleanup = refCallback(inst);// 拿到ref函数的返回值
    // 返回一个函数,作为useImperative的清理函数
    return () => {
      // 判断,若ref的返回值是一个函数
      if (typeof refCleanup === 'function') {
       // 则在useImperativeHandle调用destroy时,执行对ref的清理
        refCleanup();
      } else {
        // 否则再次调用ref函数,传值为null
        refCallback(null);
      }
    };
  } else if (ref !== null && ref !== undefined) {
   // 若ref是一个对象,则其必然是一个{current:any}的对象,否则React会报错
    const refObject = ref;
    // 执行create方法,并将其返回值赋值给re.current
    const inst = create();
    refObject.current = inst;
    // 清理函数就是将ref,current置为null
    return () => {
      refObject.current = null;
    };
  }
}

总结

useImperativeHandle的设计沿用了useLayoutEffect,其主要作用就是搭配React.forwardRef向父组件注入调用子组件的方法或数据。

相关推荐
醉城夜风~2 分钟前
手撕 C++ string:从零实现一个简易字符串类
开发语言·c++·算法
秋田君7 分钟前
QT_QT布局常用类QSplitter窗口分割类与QDockWidget窗口停靠类
开发语言·数据库·qt
老王生涯7 分钟前
rust开发环境配置-Windows & GNU
开发语言·windows·rust
Yeauty11 分钟前
自建 HLS 第一问:fMP4 还是 TS?用 Rust 在进程内把两种都跑出来
开发语言·后端·rust
颜x小31 分钟前
[C#]泛型类与泛型方法
开发语言·c++·c#
benben04432 分钟前
大模型之基于PEFT的SFT微调实战篇
开发语言·python
Lhan.zzZ44 分钟前
深入理解 windeployqt:混合 C++/Qt 项目的打包指南
开发语言·c++·qt
淼澄研学1 小时前
PyTorch深度学习实战:5个核心方法从0到1构建神经网络
前端·数据库·python
我命由我123451 小时前
Android 开发 - 广播组件(标准广播、有序广播、静态注册广播、分钟到达广播、网络变更广播...)
android·java·开发语言·网络·java-ee·android studio·android-studio
MartinYeung51 小时前
[论文学习]WASP:面向提示注入攻击的Web代理安全性基准测试
前端·网络·学习