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 来暴露一些命令式的行为。

相关推荐
wakangda33 分钟前
React Native 集成原生Android功能
javascript·react native·react.js
秃头女孩y7 小时前
【React中最优雅的异步请求】
javascript·vue.js·react.js
前端小小王13 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发13 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
不是鱼17 小时前
构建React基础及理解与Vue的区别
前端·vue.js·react.js
飞翔的渴望20 小时前
antd3升级antd5总结
前端·react.js·ant design
╰つ゛木槿1 天前
深入了解 React:从入门到高级应用
前端·react.js·前端框架
用户30587584891251 天前
Connected-react-router核心思路实现
react.js
哑巴语天雨2 天前
React+Vite项目框架
前端·react.js·前端框架
初遇你时动了情2 天前
react 项目打包二级目 使用BrowserRouter 解决页面刷新404 找不到路由
前端·javascript·react.js