React 父组件如何主动“联系”子组件

在典型的 React 数据流中,props是父子组件交互的唯一方式。要修改一个子组件,需要使用新的 props 重新渲染它。但是,在某些情况下,需要在典型数据流之外主动查看或强制修改子组件,这时候就需要使用 Refs,将 DOM Refs 暴露给父组件。

何时使用 Refs

下面是几个适合使用 refs 的情况:

  • 管理焦点,文本选择或媒体播放;
  • 触发强制动画;
  • 集成第三方 DOM 库;
  • 测量子 DOM 节点的大小或位置;
避免使用 refs 来做任何可以通过声明式实现来完成的事情,因为它会打破组件的封装。

Refs 与组件

默认情况下,ref 属性必须指向一个 DOM 元素或 class 组件,不能在函数组件上使用 ref 属性,因为它们没有实例。如果需要在函数组件中使用 ref,可以使用 forwardRef,或者将该组件转化为 class 组件。

React.forwardRef

React.forwardRef(props, ref)

  • 第二个参数 ref 只在使用 React.forwardRef 定义组件时存在。常规函数和 class 组件不接收 ref 参数,且 props 中也不存在 ref
  • Ref 转发不仅限于 DOM 组件,也可以转发 refs 到 class 组件实例中。

Ref 转发

如果使用 16.3 以上版本的 React,使用 ref 转发将 DOM Refs 暴露给父组件。Ref 转发使组件可以像暴露自己的 ref 一样暴露子组件的 ref。如果对子组件的实现没有控制权的话,只能使用 findDOMNode(),但在严格模式下已被废弃且不推荐使用。

实现 Ref 转发方式:

  • ref 和 forwardRef
  • useImperativeHandle 和 forwardRef

ref 和 forwardRef

  • 父组件创建 ref,并向下传递给子组件
  • 子组件通过 forwardRef 来获取传递给它的 ref
  • 子组件拿到 ref并向下转发该ref到自己的某一个元素上
  • 父组件通过 ref.current 获取绑定的 DOM 元素实例

缺点

  • 会把绑定 ref 元素的 DOM 直接暴露给了父组件
  • 父组件拿到 DOM 后可以进行任意的操作

例子

下面的例子使用的是 React 16.6 引入的 useRef Hook,如果是 React 16.3 版本使用 React.createRef() API, 如果更早之前的版本,使用回调形式的refs

// 子组件
import React, { forwardRef, useState } from 'react'

const BaseInfo = forwardRef((props, ref) => {
  console.log('BaseInfo --- props', props)
  console.log('BaseInfo --- ref', ref)

  const [name, setName] = useState('')
  const [age, setAge] = useState('')

  return (
    <div ref={ref}>
      姓名:
      <input onChange={val => setName(val)} />
      年龄:
      <input onChange={val => setAge(val)} />
    </div>
  )
})
// 父组件
import React, { useRef } from 'react'
import BaseInfo from './BaseInfo'

function RefTest() {
  const baseInfoRef = useRef(null)

  const getBaseInfo = () => {
    console.log('getBaseInfo --- baseInfoRef', baseInfoRef.current)
  }

  return (
    <>
      <BaseInfo
        dataSource={{ name: '混沌' }}
        ref={baseInfoRef}
      />
      <button onClick={getBaseInfo}>获取基本信息</button>
    </>
  )
}

输出

image-20211122112817260.png
点击“获取基本信息”按钮后:
image-20211122112901116.png

useImperativeHandle 和 forwardRef

useImperativeHandle 介绍

useImperativeHandle(ref, createHandle, [deps])

使用 ref 时通过useImperativeHandle自定义暴露给父组件的实例值。

通过 useImperativeHandle, 将父组件传入的 ref 和 useImperativeHandle 第二个参数返回的对象绑定到了一起。

优点

  • 只暴露给父组件需要用到的 DOM 方法;
  • 在父组件中, 调用 xxxRef.current 时,返回的是对象;

例子

// 子组件
const OtherInfo = (props, ref) => {
  console.log('OtherInfo --- props', props)
  console.log('OtherInfo --- ref', ref)

  const inputRef = useRef()
  const [school, setSchool] = useState('')

  useImperativeHandle(ref, () => {
    console.log('useImperativeHandle --- ref', ref)
    return ({
      focus: () => {
        inputRef.current.focus()
      },
      getSchool: () => {
        return inputRef.current.value
      }
    })
  }, [inputRef])

  return (
    <div>
      学校:
      <input ref={inputRef} onChange={val => setSchool(val)}/>
    </div>
  )
}

export default forwardRef(OtherInfo)
// 父组件
import React, { useRef } from 'react'
import OtherInfo from './OtherInfo'

function RefTest() {
  const otherInfoRef = useRef(null)

  const getOtherInfo = () => {
    console.log('getOtherInfo --- otherInfoRef', otherInfoRef.current)
    console.log('getOtherInfo --- otherInfoRef --- getSchool', otherInfoRef.current.getSchool())
  }

  return (
    <>
      <OtherInfo
        dataSource={{ school: '大学' }}
        ref={otherInfoRef}
      />
      <button onClick={getOtherInfo}>获取其他信息</button>
    </>
  )
}

输出
image-20211122140641074.png

把梦想放在心中

750 声望
2.4k 粉丝
0 条评论
推荐阅读
JS 事件循环(Event Loop)
理解 JavaScript 的事件循环往往伴随着宏任务和微任务、JavaScript 单线程执行过程及浏览器异步机制等相关问题,而浏览器和 NodeJS 中的事件循环实现也是有很大差别。熟悉事件循环,了解浏览器运行机制将对我们理...

时倾3阅读 370

给我实现一个前端的 Excel 导入和导出功能
前言【负责人 A】:现在报表部分基于接口的 Excel 的导入和导出功能有点慢,前端这边能不能实现一下这个功能,然后我们在比对看看效果!【切图仔 B】: 接口这边不能优化一下吗?比如排查下慢的原因什么的。【负...

熊的猫19阅读 2.6k

封面图
你知道前端水印功能是怎么实现的吗?
前一段时间由于项目需要实现水印功能,于是去了解了相关的内容后,基于 Vue 的实现了一个 v-watermark 指令完成了对应的功能,其实整体内容并不复杂!

熊的猫14阅读 1.7k

封面图
2022 你还不会微前端吗 (上) — 从巨石应用到微应用
微前端系列分为 上/下 两篇,本文为 上篇 主要还是了解微前端的由来、概念、作用等,以及基于已有的微前端框架进行实践,并了解微前端的核心功能所在,而在下篇 2022 你还不会微前端吗 (下) — 揭秘微前端核心原理...

熊的猫14阅读 1.7k

封面图
大前端必备书籍
为了方便前端开发者系统学习前端知识,搜集了前端系列电子书,帮助开发者系统梳理知识体系,深入理解前端技术。更多书单请关注Github[链接] 。CSS权威指南(第四版)上册百度云CSS权威指南(第四版)下册百度云CSS揭...

码出世界14阅读 1.4k

【WebRTC 跨端通信】React + React Native 双端视频聊天、屏幕共享
之前介绍过 WebRTC,简单来说它是一个点对点的实时通讯技术,主要基于浏览器来实现音视频通信。这项技术目前已经被广泛应用于实时视频通话,多人会议等场景。

杨成功13阅读 1.7k评论 1

封面图
2022 你还不会微前端吗 (下) — 揭秘微前端核心原理
在上篇 2022 你还不会微前端吗 (上) — 从巨石应用到微应用 中已经了解了微前端的由来和基本使用,也提到了一些相关的原理,本篇文章为下篇主要从原理层面进行解析,然后再自己实现一个包含核心部分的微前端框架。

熊的猫8阅读 1.1k

封面图

把梦想放在心中

750 声望
2.4k 粉丝
宣传栏