Using React Refs in Typescript (ok)

https://www.pluralsight.com/guides/using-react-refs-typescript

Introduction

Đối với đại đa số các thành phần React, mọi thứ có thể được thực hiện bằng cách sử dụng mô hình khai báo giao tiếp với các thành phần con bằng cách sử dụng các đạo cụ để kết xuất lại. Tuy nhiên, trong một số trường hợp, cần phải sử dụng mô hình lập trình bắt buộc và truy cập vào phần tử DOM bên dưới. Ví dụ về thời điểm cần thiết điều này có thể là đặt trọng tâm của một phần tử, truy cập các kích thước thực của phần tử hoặc sử dụng thư viện bên thứ 3 không phải React chỉ hoạt động trên các phần tử DOM. Bất kể lý do là gì, điều này có thể được thực hiện bằng cách sử dụng React refs.

Hướng dẫn này sẽ chỉ ra cách sử dụng các tham chiếu được đánh máy mạnh bằng cách sử dụng Typescript. Chúng ta sẽ xem cách sử dụng các tham chiếu từ các thành phần chức năng, sử dụng API hooks, từ các thành phần lớp trong phiên bản 16.3 trở lên, từ các thành phần lớp trong các phiên bản trước 16.3 và chuyển tiếp các tham chiếu tới các thành phần khác. Mỗi ví dụ sẽ hiển thị một div có chiều rộng là 100%, lấy tham chiếu đến phần tử div và ghi chiều rộng thực của nó vào bảng điều khiển bằng cách sử dụng thuộc tính clientWidth.

Xin lưu ý rằng, bất kể lý do sử dụng refs là gì, chúng nên được sử dụng một cách tiết kiệm và mô hình khai báo nên được sử dụng bất cứ khi nào có thể.

Functional Components

Để sử dụng các ref trong một thành phần chức năng, chúng ta tạo ref bằng hook useRef:

const divRef = React.useRef<HTMLDivElement>(null);

Đoạn mã này tạo một phiên bản RefObject có thể nhận một ref thuộc loại HTMLDivElement; RefObject có một thuộc tính duy nhất, hiện tại, có thể được đặt thành null hoặc một phiên bản HTMLDivElement. Vì chúng tôi vẫn chưa kết xuất thành phần, tham chiếu được khởi tạo thành null.

Tham chiếu được đặt khi phần tử được khai báo bằng cách sử dụng ref prop:

return <div ref={divRef} style={{ width: "100%" }} />;

Nếu một phần tử khác với div được sử dụng, trình biên dịch Typecript sẽ gây ra lỗi và divRef.current sau đó sẽ được đặt thành phần tử DOM bên dưới của div và có thể được truy cập trong mã thành phần. Đối với ví dụ này, chúng ta sẽ sử dụng ref để ghi clientWidth vào bảng điều khiển khi thành phần đã được mount:

React.useEffect(() => {
  if (divRef.current) {
    console.log(`hookRef div width: ${divRef.current.clientWidth}`);
  }
}, []);

Mã này sử dụng móc Hiệu ứng với một mảng trống làm tham số phụ thuộc để đảm bảo mã chỉ được thực thi khi thành phần được gắn kết. Nó chỉ đơn giản là kiểm tra xem ref đã được thiết lập hay chưa - trình biên dịch Typecript sẽ báo lỗi nếu câu lệnh if bị bỏ qua - và nếu có, ghi chiều rộng của nó vào bảng điều khiển.

The code for this component can be found here.

import * as React from "react";
function HookRef() {
  const divRef = React.useRef < HTMLDivElement > (null);
  React.useEffect(() => {
    if (divRef.current) {
      console.log(`hookRef div width: ${divRef.current.clientWidth}`);
    }
  }, []);
  return <div ref={divRef} style={{ width: "100%", height: "30px", backgroundColor: "orange" }} />;
}
export default HookRef;

Hoặc

import * as React from "react";
function HookRef() {
  const divRef = React.useRef < HTMLDivElement > ();
  React.useEffect(() => {
    if (divRef.current) {
      console.log(`hookRef div width: ${divRef.current.clientWidth}`);
    }
  }, []);
  return <div ref={divRef} style={{ width: "100%", height: "30px", backgroundColor: "orange" }} />;
}
export default HookRef;

Class Components

Khi sử dụng các thành phần lớp, các tham chiếu vẫn có sẵn. Trong một lớp, nó có thể được tạo dưới dạng trường lớp, chúng ta tạo ref bằng cách sử dụng createRef

divRef = React.createRef<HTMLDivElement>();

Như trong ví dụ về thành phần chức năng ở trên, điều này đặt this.divRef thành một RefObject có tham chiếu kiểu HTMLDivElement. Trong trường hợp này, ref được khởi tạo thành null mà không cần phải chỉ định bất kỳ tham số nào.

Trong phương thức render, ref prop được sử dụng giống như ví dụ trước:

render() {
  return <div ref={this.divRef} style={{ width: "100%" }} />;
}

Phần tử DOM hiện có thể được truy cập từ bên trong lớp bằng this.divRef.current, vì vậy nó có thể được sử dụng khi thành phần đã được gắn kết như sau:

componentDidMount() {
  if (this.divRef.current) {
    console.log(`createRefRef div width: ${this.divRef.current.clientWidth}`);
  }
}

Như trong ví dụ trước, mã kiểm tra xem ref đã được thiết lập và ghi chiều rộng vào bảng điều khiển.

The code for this component can be found here.

import * as React from "react";
class CreateRef extends React.Component {
  divRef = React.createRef < HTMLDivElement > ();
  componentDidMount() {
    if (this.divRef.current) {
      console.log(`createRefRef div width: ${this.divRef.current.clientWidth}`);
    }
  }
  render() {
    return <div ref={this.divRef} style={{ width: "100%", height: "30px", backgroundColor: "blue" }} />;
  }
}
export default CreateRef;

Ref Callback

Hàm createRef đã được giới thiệu trong phiên bản React 16.3. Vì vậy, khi sử dụng phiên bản trước, một kỹ thuật khác cần được sử dụng để truy cập vào một ref. Trong các ví dụ trước, ref prop đã được đặt thành một phiên bản của RefObject nhưng nó cũng có thể được đặt thành một phương thức gọi lại chấp nhận một tham số của phần tử DOM. Lệnh gọi lại này sau đó có thể được sử dụng để đặt một trường lớp cho phần tử:

divRef: HTMLDivElement | null = null;
setDivRef = (element: HTMLDivElement) => {
  this.divRef = element;
};

Vì không có RefObject, trường lớp được nhập là HTMLDivElement | null, nghĩa là nó có thể được đặt thành null hoặc HTMLDivElement và được khởi tạo thành null.

Sau đó, trong phương thức kết xuất, đề xuất tham chiếu được đặt thành lệnh gọi lại này:

render() {
  return <div ref={this.setDivRef} style={{ width: "100%" }} />;
}

Finally in componentDidMount the ref can be accessed:

componentDidMount() {
  if (this.divRef) {
    console.log(`callbackRef div width: ${this.divRef.clientWidth}`);
  }
}

The code for this component can be found here.

import * as React from "react";
class CallbackRef extends React.Component {
  divRef: HTMLDivElement | null = null;
  setDivRef = (element: HTMLDivElement) => {
    this.divRef = element;
  };
  componentDidMount() {
    if (this.divRef) {
      console.log(`callbackRef div width: ${this.divRef.clientWidth}`);
    }
  }
  render() {
    return <div ref={this.setDivRef} style={{ width: "100%", height: "30px", backgroundColor: "red" }} />;
  }
}
export default CallbackRef;

Forwarding Refs

Đôi khi có thể hữu ích khi truy cập một phần tử DOM bên trong một thành phần React con từ bên trong phần tử gốc. Để làm điều này, chúng ta có thể sử dụng các ref chuyển tiếp.

Để chấp nhận một ref chuyển tiếp, thành phần con được tạo bằng cách sử dụng hàm forwardRef chấp nhận hai tham số - props và ref. Sau đó, tham chiếu có thể được đặt khi phần tử được khai báo theo cùng một cách, như thể nó đã được tạo trong thành phần:

const Forward = React.forwardRef((props, ref: React.Ref<HTMLDivElement>) => (
  <div ref={ref} style={{ width: "100%" }} />
));

Thay vì, như trong các ví dụ trước, thuộc loại RefObject, ref được chuyển tiếp thuộc loại Ref.

Việc truy cập vào ref được chuyển tiếp giống hệt như sử dụng ref thông thường - tạo ref, đặt ref prop và truy cập nó bằng cách sử dụng thuộc tính hiện tại:

function ForwardRefRef() {
  const divRef = React.useRef<HTMLDivElement>(null);
  React.useEffect(() => {
    if (divRef.current) {
      console.log(`forwardRefRef div width: ${divRef.current.clientWidth}`);
    }
  });
  return <Forward ref={divRef} />;
}

The code for this component can be found here.

import * as React from "react";
const Forward = React.forwardRef((props, ref: React.Ref<HTMLDivElement>) => (
  <div ref={ref} style={{ width: "100%", height: "30px", backgroundColor: "green" }} />
));
function ForwardRef() {
    const divRef = React.useRef<HTMLDivElement>(null);
    React.useEffect(() => {
        if (divRef.current) {
            console.log(`forwardRefRef div width: ${divRef.current.clientWidth}`);
        }
    });
    return <Forward ref={divRef} />;
}
export default ForwardRef;

Last updated

Was this helpful?