【译】React Refs中使用TypeScript

先放上原文连接html

今年四、5月的杭州天气老是让人摸不着头脑,可能也是今年疫情的缘由,彷佛并无太多春天的感受;虽然温度是慢慢的升上来了,但感受没有春天的那种万物复苏,处处鲜花怒放的感受;不知道是今年疫情的缘由,仍是视线被俗事一叶障目,总之一切都不太像想象中的样子。闲话少叙吧,搬运一篇Martin Hochel的文章node

文章使用如下版本react

{
  "@types/react": "16.4.7",
  "@types/react-dom": "16.0.6",
  "typescript": "3.0.1",
  "react": "16.4.2",
  "react-dom": "16.4.2"
}
复制代码

源码地址git

常常会收到询问typescript中如何合适的定义react refs的类型的问题,我没有找到有人写过关于这个问题的资料,就写了这篇文章帮助那些新接触react和typescript的人。github

免责声明: 这篇译文彻底是凭本身兴趣翻译,详情请参考原文和React Ref的官方文档typescript

什么是Refs

Refs provide a way to access DOM nodes or React elements created in the render method. Refs提供了一种在React中访问Dom节点的方式json

建立Refs

class MyComponent extends Component {
  private myRef = createRef()
  render() {
    return <div ref={this.myRef} /> } } 复制代码

组件这样定义会收到一个编译报错api

[ts]
Type 'RefObject<{}>' is not assignable to type 'RefObject<HTMLDivElement>'.
复制代码

为何会出现这个报错呢,ts在jsx文件中有着很是优秀的类型推断,咱们在一个<div>上增长了ref,ts就推断出须要使用HTMLDivElement类型进行接收,咱们能够经过下面的写法解决这个问题。咱们先看一下对React.createRef的定义:数组

// react.d.ts
function createRef<T>(): RefObject<T> // react.d.ts interface RefObject<T> {
  // immutable
  readonly current: T | null
}
复制代码

返回的RefObject<T>是泛型;由此咱们就能够知道怎么解决上述验证报错的问题了: 安全

使用Refs

咱们能够直接经过定义在ref上的current属性来访问这个node,以下

const node = this.myRef.current
复制代码

可是呢,这个node多是null,因此咱们在使用这个值来进行一些操做的时候,使用下面的代码是不行的,会收到ts的报错:

class MyComponent extends Component {
  private myRef = React.createRef<HTMLDivElement>()
  focus() {
    const node = this.myRef.current
    node.focus()
  }
}

[ts] Object is possibly 'null'.
const node: HTMLDivElement | null
复制代码

咱们先看一下React文档是怎么定义current的:

React will assign the current property with the DOM element when the component mounts, and assign it back to null when it unmounts. ref updates happen before componentDidMount or componentDidUpdate lifecycle hooks. 当组件挂载componentDidMount时,React会将dom节点赋值给current,而当componentDidUpdate时,current的值被修改成null;

这就是ts想经过编译错误想提醒你的信息,你须要进行一些安全措施,使用if进行非空判断时必要的,以保证程序不会出现运行时错误;

这样咱们就获得了HTMLDivElement DOM api的自动提醒,仍是很香的

给自定义Class组件添加Ref

和上面的栗子相似,咱们若是想在挂载自定义组件的时候就获取焦点,咱们能够经过ref访问到自定义组件的实例,调用组件的focus方法,就能够实现:

import React, { createRef, Component } from 'react'
class AutoFocusTextInput extends Component {
  // create ref with explicit generic parameter 
  // this time instance of MyComponent
  private myCmp = createRef<MyComponent>()
  componentDidMount() {
    // @FIXME
    // non null assertion used, extract this logic to method!
    this.textInput.current!.focus()
  }
  render() {
    return <MyComponent ref={this.textInput} /> } } 复制代码

⚠️

  • 这种方式仅仅适用于声明为class的自定义组件
  • 咱们能够访问全部的实例方法

传递refs到DOM components

定义一个FancyButton组件,渲染一个原生的button到页面上

Ref forwarding是组件一个可选的的特性。它能够接受上层组件传递下来的ref,而后传递给本身的子组件。

在下面的例子中,咱们给组件加上Forwarding Refs,这样在必要的时候咱们就能够经过ref直接访问这个组件内的button节点,就像咱们直接使用button节点同样

这里究竟是什么实现的呢?

  • 咱们建立并export一个Ref类型给咱们组件的调用方
  • 使用forwardRef,咱们获取到ref,并传递给子组件,这是一个包含两个参数的普通函数
// react.d.ts
function forwardRef<T, P = {}>(
  Component: RefForwardingComponent<T, P>
): ComponentType<P & ClassAttributes<T>>

// 这里的T,P
1:T是DOM的类型
2:P是传递的Props
3:返回值的定义是混合了props值和ref类型的组件的定义
复制代码

这样咱们就能够类型安全的调用了:

是否是很优雅

Refs和函数组件

由于函数组件没有实例,你可能不会在函数组件上使用ref属性;可是当你在函数组件内部的时候,仍是能够经过ref来访问Dom节点或class组件的

Forwarding Refs

Ref forwarding是一种将ref钩子自动传递给组件的子孙组件的技术

高阶组件中使用Forwarding refs

官方文档关于高阶组件使用Forwarding refs的方式仍是比较复杂的;简答来讲,咱们只要用forwardRef API包一层你的组件就行了

return forwardRef((props, ref) => {
  return <LogProps {...props} forwardedRef={ref} /> }) 复制代码

下面是class组件方式实现的FancyButton

这是咱们想要如何使用这个组件

最后,咱们经过ref forwarding实现这个高阶组件

使用ref forwarding的高阶组件须要咱们明确参数类型

const EnhancedFancyButton = withPropsLogger<
  FancyButton, 
  FancyButtonProps
>(FancyButton)
复制代码

这样咱们就能够类型安全的使用了

以上! 最后的惯例,贴上个人博客,欢迎关注

相关文章
相关标签/搜索