x-note
  • Introduction
  • JavaScript
    • JavaScript 作用域链
    • JavaScript 数据结构与类型
    • JavaScript 原型
    • JavaScript this 关键字
    • JavaScript 函数
    • JavaScript delete 运算符
    • JavaScript 内存管理与垃圾回收
    • JavaScript 严格模式与混乱模式
    • JavaScript 数字精度丢失
    • JavaScript 并发模型
    • 利用原型链实现继承
  • ECMAScript
    • ECMAScript 6 变量及常量的声明
    • ECMAScript 6 变量的解构赋值
    • ECMAScript 6 Promise 对象
    • ECMAScript 6 Symbol
    • ECMAScript 6 Proxy
    • ECMAScript 6 Reflect
    • ECMAScript 6 new.target
    • ECMAScript 6 Set 和 WeakSet
    • ECMAScript 6 Map 和 WeakMap
    • ECMAScript 6 Iterator
    • ECMAScript 6 Generator
    • ECMAScript 6 class
    • ECMAScript 7
    • ECMAScript 8 async 函数
    • ECMAScript 8 内存共享与原子性
    • ECMAScript 8 Others
    • ECMAScript 2018
    • ECMAScript 2019
  • CSS
    • CSS 块格式化上下文(BFC)
    • CSS 盒模型
    • CSS 外边距合并
    • CSS Float
    • CSS Position
    • CSS Border-Image
    • CSS BEM
    • CSS 表布局详解
    • 页面布局之单列布局
    • 页面布局之多列布局
  • React
    • React 组件的生命周期
    • React 虚拟 DOM
    • React Reconciliation
    • React Diff 算法核心
    • React Fiber
    • React Scheduling
    • React Context API
    • React Refs
    • React HMR
    • React Hook
  • VUE
    • VUE 响应式系统
    • VUE 渲染机制
    • 关于 Vue 的思考
  • Webpack
    • Webpack 基本概念
    • Webpack HMR
  • Babel
    • @babel/preset-env
  • WEB
    • WEB 基础知识及概念
      • 屏幕测量单位
      • 重绘与重排
      • 前端模块化系统
      • WEB 客户端存储
      • 浏览器的渲染过程
    • WEB 性能优化
      • WEB 性能指标
      • WEB 图片优化
      • 懒加载资源
    • WEB 安全
      • XSS
      • XSRF
      • 点击劫持
      • 同源策略(Same Origin Policy,SOP)
    • WEB 解决方案
      • webp 兼容方案
      • WEB 拖拽实现方案
    • WEB SEO
  • Git
    • Git 工作流
    • Git 内部原理
  • 传输协议
    • UDP
      • UDP 基本概念
    • TCP
      • TCP 基本概念
    • HTTP
      • HTTP 基础
      • HTTP 缓存
      • HTTP-2
      • HTTP-3
      • HTTPS
      • 自定义 HTTPS 证书
  • Protocol Buffers
    • Protocol Buffers 基础
  • gRPC
    • gRPC 简介
    • gRPC 基础概念
    • GRPC with GraphQL and TypeScript
  • 正则表达式
    • 正则表达式基础
    • 正则表达式的悲观回溯
  • 基础算法
    • 冒泡排序
    • 插入排序
    • 选择排序
    • 快速排序
    • 归并排序
    • 希尔排序
    • 堆排序
    • 桶排序
    • 计数排序
    • 基数排序
    • 二叉树的遍历
    • 动态规划
    • 回溯
  • 压缩算法
    • HPACK
    • QPACK
  • 设计模式
    • DDD
      • 模型元素的模式
    • 常见设计模式
      • 工厂方法
      • 抽象工厂
      • 构造器
      • 原型
      • 单例模式
      • 适配器模式
      • 桥接模式
      • 组合模式
      • 外观模式
      • 享元模式
      • 代理模式
      • 责任链模式
      • 命令模式
      • 迭代器模式
      • 中介者模式
      • 备忘录模式
      • 观察者模式
      • 状态模式
      • 策略模式
      • 模版方法模式
      • 访问者模式
      • 依赖注入
    • MVC
    • MVP
    • MVVM
  • 颜色空间
    • LCH
由 GitBook 提供支持
在本页
  • 使用 Refs 的条件
  • 创建和访问 Refs
  • React.createRefs
  • 回调 Refs
  • 字符串类型的 Refs
  • 转发 Refs(Forwarding Refs)
在GitHub上编辑
  1. React

React Refs

React 提供了 refs 用于访问 DOM 节点或在 render 方法中创建的 React 元素。尽管在 React 单向数据流中, props 是父子组件交互的唯一方式。但是,某些特殊情况下仍需要在典型数据流外强制修改子组件或是 DOM 元素。

Refs 不能用在无状态组件中

使用 Refs 的条件

  • 处理焦点、文本选择或媒体控制

  • 触发强制动画

  • 集成第三方 DOM 库(AMap、Google Map)

**不要过度使用 Refs。**实际上可以使用 Refs 很“方便”的去处理一些问题,例如通过 Refs 更新子组件的状态,但是提升 state 所在的组件层级或许会是更合适的做法。

如可以通过声明式实现,则尽量避免使用 Refs

创建和访问 Refs

目前有三种方式能够创建 Refs,通过不同方式创建的 Refs 访问方式也不同。

  • React.createRefs() API

  • 回调 Refs

  • 字符串类型的 Refs

React.createRefs 创建的 Ref 在componentDidMount()之前的值均是object 只不过该对象的 current 属性此时为 null,其余两种则是 undefined

React.createRefs

React v16.3.0 新增了React.createRefs() 用于创建 Refs。当一个 ref 属性被传递给一个render函数中的元素时,可以使用 ref 中的current属性对节点的引用进行访问。

class MyComponent extends React.Component {
    constructor(props) {
        super(props);
        this.myRef = React.createRef();
    }
    componentWillMount() {
        console.log({ ...this.refGenerateByAPI}); // { current: null }
    }
    componentDidMount() {
        this.myRef.current.focus();
    }
    render() {
        return (
            <input ref={this.myRef} type="text" />
        )
    }
}

回调 Refs

传入回调函数,并绑定到当前实例上。回调函数传入的值就是子元素的实例,可以直接访问。

class MyComponent extends React.Component {
    constructor(props) {
        super(props);
        this._bindRef = this._bindRef.bind(this);
    }
    _bindRef(input) {
        this.myRef = input; 
    }
    componentWillMount() {
        console.log(this.myRef); // undefined
    }
    componentDidMount() {
        this.myRef.focus();
    }

    render() {
        return (
            <input ref={this._bindRef} type="text" />
        )
    }
}

如果 ref 回调以内联函数的方式定义,在更新期间它会被调用两次,第一次参数是 null ,之后参数是 DOM 元素。这是因为在每次渲染中都会创建一个新的函数实例。因此,React 需要清理旧的 ref 并且设置新的。通过将 ref 的回调函数定义成类的绑定函数的方式可以避免上述问题,但是大多数情况下无关紧要。

字符串类型的 Refs

字符串类型的 Refs 是,最早的使用 refs 的方式,由于 String 类型的 refs 存在某些问题,这种使用方式非常有可能在未来的版本中被废弃。

通过这种方式声明的 Refs 最终能通过组件实例的refs属性的{string}属性进行访问,

class MyComponent extends React.Component {
    constructor(props) {
        super(props);
    }
    componentWillMount() {
        console.log(this.refs.myRef); // undefined
    }
    componentDidMount() {
        this.refs.myRef.focus();
    }
    render() {
        return (
            <input ref="myRef" type="text" />
        )
    }
}

转发 Refs(Forwarding Refs)

假设我们有一组件 A 经过context.Consumer之类的高阶组件或是 withRouter之类的函数封装成了 B,此时又希望能够使用 B 组件的时候,能够操作 A 的实例或对应的 DOM。

比如,现有一组件实现如下,并且希望对 ThemeInput 的实例进行 focus 的操作

function ThemeInput(props) {
  return (
    <input className="theme-input" />
  );
}

这种情况下,可以使用 React v16.3.0 推出的新 API —— React.forwardRefs(fn) 重新实现 ThemeInput

const ThemeInput = React.forwardRef((props, ref) => (
  <input ref={ref} className="theme-input" />
));

class XXXForm extends React.Component {
  consturctor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  componentDidMount() {
    this.myRef.current.focus();
  }
  render() {
    return (
      <ThemeInput ref={this.myRef} />
    );
  }
}

上述示例具体行为:

  1. 我们通过调用 React.createRef 创建一个 React ref 并将其分配给一个 ref 变量。

  2. 通过将它指定为 JSX 属性,我们将我们的 ref 传递给 <ThemeInput ref = {ref}>。

  3. React 将 ref 传递给 forwardRef 中的(props,ref) => {}函数作为第二个参数。

  4. 我们通过指定它作为一个JSX属性,将这个ref参数转发给 <input ref = {ref}>。

  5. 当ref被附加时,ref.current 将指向<input>DOM节点。

第二个参数只有在您使用 React.forwardRef 调用定义组件时才存在。 常规的功能或类组件不会收到参数参数,并且参考也不可用于道具中。

引用转发不限于DOM组件。 您也可以将引用转发给类组件实例。

forwardRef 在 HOC 中的应用的 React 官方示例,通过额外的 forwardedRef属性结合 React.forwardedRef() 的方式,更加轻易的实现传递 refs 的功能

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;

      return <Component ref={forwardedRef} {...rest} />;
    }
  }
  
  return React.forwardRef((props, ref) => (
    <LogProps {...props} forwardedRef={ref} />
  ));
}
上一页React Context API下一页React HMR

最后更新于6年前