React + TypeScript:元素引用的传递


React 中需要操作元素时,可通过 findDOMNode() 或通过 createRef() 创建对元素的引用来实现。前者官方不推荐,所以这里讨论后者及其与 TypeScript 结合时如何工作。

React 中的元素引用

正常的组件中,可通过创建对元素的引用来获取到某元素然后进行相应操作。比如元素加载后将焦点定位到输入框。

class App extends Component {
  constructor(props){
    super(props);
    this.inputRef = React.createRef();
  }

  componentDidMount(){
    this.inputRef.current.focus()
  }

  render() {
    return (
      <div className="App">
        <input type="text" ref={this.inputRef}/>
      </div>
    );
  }
}

创建对元素的引用是通过 React.createRef() 方法完成的。使用的时候,通过其返回对象身上的 current 属性可访问到绑定引用的元素。

React 内部对引用的 current 赋值更新发生在 componentDidMountcomponentDidUpdate 生命周期之前,即存在使用的时候引用未初始化完成的情况,所以 current 不一定有值。好的做法是使用前先判空。

if(this.inputRef.current){
    this.inputRef.current.focus()
}

在上面的示例中,之所以不用判空是因为我们在 componentDidMount 生命周期中使用,此时元素已经加载到页面,所以可以放心使用。

组件中引用的传递

对于原生 DOM 元素可以像上面那样创建引用,但对于自己写的组件,则需要使用 forwardRef() 来实现。

假如你写了个按钮组件,想要实现像上面那样,让使用者可通过传递一个 ref 属性来获取到组件中原生的这个 <button> 元素以进行相应的操作。

button.jsx

const FancyInput = props => <input type="text" className="fancy-input" />;

添加 ref 支持后的按钮组件:

button.jsx

const FancyInput = React.forwardRef((props, ref) => {
  return <input type="text" ref={ref} className="fancy-input" />;
});

forwardRef 接收一个函数,函数的入参中第一个是组件的 props,第二个便是外部传递进来的 ref 引用。通过将这个引用在组件中绑定到相应的原生 DOM 元素上,实现了外部直接引用到组件内部元素的目的,所以叫 forwardRef(传递引用)。

使用上面创建的 FancyInput,在组件加载后使其获得焦点:

class App extends Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }

  componentDidMount() {
    if (this.inputRef.current) {
      this.inputRef.current.focus();
    }
  }

  render() {
    return (
      <div className="App">
-        <input type="text" ref={this.inputRef}/>
+        <FancyInput ref={this.inputRef} />
      </div>
    );
  }
}

TypeScript 中传递引用

先看正常情况下,对原生 DOM 元素的引用。还是上面的示例:

class App extends Component<{}, {}> {
  private inputRef = React.createRef();

  componentDidMount() {
    /** ?? Object is possibly ‘null‘ */
    this.inputRef.current.focus();
  }

  render() {
    return (
      <div className="App">
        {/* ?? Type ‘{}‘ is missing the following properties from type ‘HTMLInputElement‘:... */}
        <input type="text" ref={this.inputRef} />
      </div>
    );
  }
}

像上面那样创建并使用存在两个问题。

一个是提示我们的引用无法赋值到 <input>ref 属性上,类型不兼容。引用需要与它真实所指代的元素类型相符,这正是 TypeScript 类型检查为我们添加的约束。这个约束的好处是,我们在使用引用的时候,就知道这个引用真实的元素类型,TypeScript 会自动提示可用的方法和属性,同时防止调用该元素身上没有的属性和方法。这里修正的方法很简单,如果 hover 或 F12 查看 React.createRef() 的方法签名,会发现它是个泛型方法,支持传递类型参数。

function createRef<T>(): RefObject<T>;

所以上面创建引用时,显式指定它的类型。

- private inputRef = React.createRef();
+ private inputRef = React.createRef<HTMLInputElement>();

第二个问题是即使在 componentDidMount 生命周期中使用,TypeScript 仍然提示 current 的值有可能为空。上面讨论过,其实此时我们知道它不可能为空的。但因为 TypeScript 无法理解 componentDidMount,所以它不知道此时引用其实是可以安全使用的。解决办法当然是加上判空的逻辑。

  componentDidMount() {
+    if(this.inputRef.current){
      this.inputRef.current.focus();
+    }
  }

还可通过变量后添加 ! 操作符告诉 TypeScript 该变量此时非空。

  componentDidMount() {
-      this.inputRef.current.focus();
+      this.inputRef.current!.focus();
  }

修复后完整的代码如下:

class App extends Component<{}, {}> {
  private inputRef = React.createRef<HTMLInputElement>();

  componentDidMount() {
    this.inputRef.current!.focus();
  }

  render() {
    return (
      <div className="App">
        <input type="text" ref={this.inputRef} />
      </div>
    );
  }
}

React + TypeScript 组件引用的传递

继续到组件的情况,当需要引用的元素在另一个组件内部时,还是通过 React.forwardRef()

这是该方法的签名:

function forwardRef<T, P = {}>(Component: RefForwardingComponent<T, P>): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;

可以看到,方法接收两个类型参数,T 为需要引用的元素类型,我们示例中是 HTMLInputElementP 为组件的 props 类型。

所以添加引用传递后,FancyInput 组件在 TypeScript 中的版本应该长这样:

const FancyInput = React.forwardRef<HTMLInputElement, {}>((props, ref) => {
  return <input type="text" ref={ref} className="fancy-input" />;
});

使用组件:

class App extends Component<{}, {}> {
  private inputRef = React.createRef<HTMLInputElement>();

  componentDidMount() {
    this.inputRef.current!.focus();
  }

  render() {
    return (
      <div className="App">
        <FancyInput ref={this.inputRef} />
      </div>
    );
  }
}

相关资源

原文地址:https://www.cnblogs.com/Wayou/p/react_typescript_forwardref.html

时间: 2024-09-28 22:12:44

React + TypeScript:元素引用的传递的相关文章

delphi的一些语法知识 以及参数传递问题,按引用方式传递参数,按值方式传递参数

//delphi中exit,abort,break,continue 的区别 exit: 退出函数体abort: 遇到异常,安静处理,就是不显示不提示break: 退出当前循环体,包括for ,while, repeat等循环体continue: 结束循环内的本次处理,继续从循环体的开始位置继续执行 Exit 是跳出当前代码块,也就是当前函数,跳出后是要继续向下执行的(如果有后续代码). Abort 是从 EAbort 过来的,可以激发 exception,其实质就是 Abort = Raise

JS 中没有按地址(引用)传递,只有按值传递

很多人,包括我,受书本知识消化不彻底的影响,认为 JS 中参数有两种传递方式:数字.字符串等按值传递:数组.对象等按地址(引用)传递.对此种观点,我们要谨慎. var v1 = []var v2 = {};var v3 = {};function foo(v1, v2, v3){    v1 = [1];    v2 = [2];    v3 = {a:3}} foo(v1, v2, v3);alert (v1); // 空白 alert (v2); // [object Object] ale

react + typescript 学习

react,前端三大框架之一,也是非常受开发者追捧的一门技术.而 typescript 是 javascript 的超集,主要特点是对 类型 的检查.二者的结合必然是趋势,不,已经是趋势了.react 文档.typescript 文档都看过,例子也敲过了,对此也都有了一定的理解,但是把二者很好的结合在一起,还是遇到了一些问题.纯粹记录一些,当然也希望有优秀资源的,提供一下,分享一下.提前道谢了~ 学习曲线 首先,想到的是到 官网,看相关文档,会系统些. typescript 中文网 jsx ty

React + TypeScript 默认 Props 的处理

React 中的默认 Props 通过组件的 defaultProps 属性可为其 Props 指定默认值. 以下示例来自 React 官方文档 - Default Prop Values: class Greeting extends React.Component { render() { return ( <h1>Hello, {this.props.name}</h1> ); } } // Specifies the default values for props: Gr

React+TypeScript

新建项目 新建工程文件夹 1 $ mkdir TypeScriptDemo && cd TypeScriptDemo 初始化工程 除了package name 其他都默认敲回车即可 1 2 3 4 $ npm init package name: (TypeScriptDemo) TypeScriptDemo ... Is this ok? (yes) yes 组织目录结构 src目录存放工程代码,dist最终由webpack生成 1 2 3 4 TypeScriptDemo/ ├─ di

Java值传递以及引用的传递、数组的传递!!

转(http://blog.csdn.net/niuniu20008/article/details/2953785) 许多编程语言都有2种方法将参数传递给方法------按值传递和按引用传递. 与其他语言不同,Java不允许程序员选择按值传递还是按引用传递各个参数,基本类型(byte--short--int--long--float--double--boolean--char)的变量总是按值传递.就对象而言,不是将对象本身传递给方法,而是将对象的的引用或者说对象的首地址传递给方法,引用本身是

React中jquery引用

在项目中使用了table元素,但点击tr元素时要添加click事件,同时table的行元素会在react中获取到数据时动态渲染,此时想到之前在jQuery用使用on来绑定事件 例如jQuery中绑定的方式: //订单列表table点击时切换行的样式$(".classname table").on("click", 'tr', function () { var $select = $(this); .....}); 于是在react中引用jQuery,使用npm i

从对象的引用与传递到java的反射机制

正所谓“知己知彼”百战不殆,要想弄清楚引用传递,首先要先弄清楚两块内存(即栈内存,堆内存) 1.简单认识栈内存和堆内存 栈内存:存放的东西,可以理解为一个整型变量(只能保存一个数值),其中保存的是一块(只能是一块)堆内存空间的内存地址数值,为了便于理解就先把它当成对象的名字吧! 堆内存:存放的东西,可以理解为一个对象的具体信息,每块堆内存的开辟都是使用new关键字完成的 通过下面的代码和图不难看出对象的创建和在各内存的分配是怎样完成的 package com.shxt.bishi; class

浅析C#值传递与引用值传递

先上一段代码. using UnityEngine; using System.Collections; public class TypePassing : MonoBehaviour { public Dog dog = new Dog (1, "xiaobai") ; //调用默认构造函数给struct里所有字段一个缺省值,对dog来说是null public Cat cat = new Cat ();//貌似在这里不能直接赋值dog属性 int i = 0; void Star