React hooks - forwardRef+useImperativeHandle

forwardRef+useImperativeHandle

React.forwardRef用法

1.创建一个 能够接受到ref属性的React 组件。

ref 用来获取实例,但函数组件不存在实例,所以ref无法获取函数式组件的实例

javascript 复制代码
React.forwardRef((props, ref) => {子函数组件})
useImperativeHandle用法
  1. 按需向外暴露成员
  2. 控制成员暴露的粒度
    ref 获取 DOM 实例,会全面暴露 DOM 实例上的 API,导致外部使用 ref 时自由度太大。使用useImperativeHandle控制 ref 暴露颗粒度,只暴露主要的功能函数。
javascript 复制代码
useImperativeHandle(ref, createHandle, [deps])
useImperativeHandle(ref, () => 自定义ref对象, [依赖项数组])

// 第一个参数为父组件传递的 ref

// 第二个参数是一个函数,子组件将自己内部的方法或值作为对象返回,并自动绑定到父组件定义的并传给子组件的 ref 上

// 第三个参数是函数依赖的值(可选),createHandle中用到的子组件内部定义的变量也需要作为依赖项

第三个参数的用法
  1. 空数组:只在子组件首次被渲染时,执行 useImperativeHandle 中的 fn 回调,从而把 return 的对象作为父组件接收到的 ref。
  2. 依赖项数组:子组件首次被渲染时,会依赖项改变时,会执行 useImperativeHandle 中的 fn 回调,从而让父组件通过 ref 能拿到依赖项的新值。
  3. 省略依赖项数组(省略第三个参数):此时,子组件内任何 state 的变化,都会导致回调的重新执行,因为每次state改变都会让函数组件rendered,会重新调用一次回调
React.forwardRef与useImperativeHandle配合使用

React.forwardRef()包裹子组件

子组件中使用useImperativeHandle 向外按需暴露子组件内的成员

父组件中使用childRef.current调用暴露出来的值的方法

javascript 复制代码
<!-- const Child: React.FC = () => { -->
<!-- 被包装的函数式组件不再是 React.FC 类型,接收两个参数 props 和转发过来的 ref -->
const Child = React.forwardRef((props, ref) => { 
  const [count, setCount] = useState(0)
  const add = (step: number) => {
    setCount((prev) => (prev += step))
  }
  // 1. 向外暴露一个空对象
  // useImperativeHandle(ref, () => ({}))
  // 2. 向外暴露一个对象,其中包含了 name 和 age 两个属性
  // useImperativeHandle(ref, () => ({ name: 'liulongbin', age: 22 }))
  // 3.向外暴露 count 的值和 setCount 函数
  // useImperativeHandle(ref, () => ({ count, setCount }))
  // 4.控制成员暴露的粒度,向外暴露reset方法,内部写死只能设置count为0,外部不能随意写入count
  useImperativeHandle(ref, () => ({ count, reset:()=>setCount(0) }))
  return (
    <>
      <h3>Child 子组件 {count}</h3>
      <button onClick={() => add(-1)}>-1</button>
      <button onClick={() => add(1)}>+1</button>
    </>
  )
} 

当子组件没有使用useImperativeHandle暴露出自己的任何东西时childRef.current为null

当向外暴露{}时,childRef.current为{}

当向外暴露{ name: 'liulongbin', age: 22 }时,childRef.current为{ name: 'liulongbin', age: 22 }

javascript 复制代码
export const Father: React.FC = () => {
  const childRef = useRef<count: number, setCount: (value: number)>()
  const onShowRef = () => {
    console.log(childRef.current) 
  }
  // 重置按钮的点击事件处理函数
  const onReset = () => {
    // childRef.current?.setCount(0) // 可以设置count为任何值
    childRef.current?.reset() // 只能设置count为0
  } 
  return (
    <>
      <h1>Father 父组件</h1>
      {/* 点击按钮,打印 ref 的值 */}
      <button onClick={onShowRef}>show Ref</button>
      {/* 点击按钮,重置数据为 0 */}
      <button onClick={onReset}>重置</button>
      <hr />
      <Child ref={childRef} />
    </>
  )
} 
注意事项

1:不要滥用 ref,可以通过 prop 实现,就不应该使用 ref。 仅在你没法通过 prop 来表达时才使用 ref:例如,滚动到指定节点、聚焦某个节点、触发一次动画,以及选择文本等等。

2:不应该从一个 Model 组件暴露出 {open, close} 这样的命令式句柄,最好是像 这样,将 isOpen 作为一个 prop。副作用可以帮你通过 prop 来暴露一些命令式的行为。

相关推荐
xd000022 小时前
12.vite,webpack构建工具
react.js
WildBlue3 小时前
🚀 React初体验:从“秃头程序员”到“数据魔法师”的奇幻漂流
前端·react.js
哼唧唧_3 小时前
使用 React Native 开发鸿蒙(HarmonyOS)运动健康类应用的系统化准备工作
react native·react.js·harmonyos·harmony os5·运动健康
_一两风5 小时前
React 组件化开发:从项目创建到组件通信
react.js
工呈士5 小时前
Context API 应用与局限性
前端·react.js·面试
钟看不钟用5 小时前
React(1)——渲染完整流程
react.js
胡gh5 小时前
深入理解React,了解React组件化,脱离”切图崽“,迈向高级前端开发师行列
前端·react.js
谢尔登8 小时前
【React】React 18 并发特性
前端·react.js·前端框架
Joker`s smile8 小时前
使用React+ant Table 实现 表格无限循环滚动播放
前端·javascript·react.js
国家不保护废物8 小时前
🌟 React 魔法学院入学指南:从零构建你的第一个魔法阵(项目)!
前端·react.js·架构