Deprecated

Trong React 19, forwardRef không còn cần thiết nữa. Thay vào đó, hãy truyền ref như một prop.

forwardRef sẽ bị loại bỏ trong một bản phát hành trong tương lai. Tìm hiểu thêm tại đây.

forwardRef cho phép component của bạn hiển thị một DOM node cho component cha bằng một ref.

const SomeComponent = forwardRef(render)

Tham khảo

forwardRef(render)

Gọi forwardRef() để cho phép component của bạn nhận một ref và chuyển nó đến một component con:

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
// ...
});

Xem thêm các ví dụ bên dưới.

Tham số

  • render: Hàm render cho component của bạn. React gọi hàm này với các props và ref mà component của bạn nhận được từ component cha. JSX bạn trả về sẽ là đầu ra của component của bạn.

Giá trị trả về

forwardRef trả về một React component mà bạn có thể render trong JSX. Không giống như các React component được định nghĩa là các hàm thuần túy, một component được trả về bởi forwardRef cũng có thể nhận một prop ref.

Lưu ý

  • Trong Strict Mode, React sẽ gọi hàm render của bạn hai lần để giúp bạn tìm các tạp chất vô tình. Đây là hành vi chỉ dành cho quá trình phát triển và không ảnh hưởng đến production. Nếu hàm render của bạn là thuần túy (như nó phải vậy), điều này sẽ không ảnh hưởng đến logic của component của bạn. Kết quả từ một trong các lệnh gọi sẽ bị bỏ qua.

Hàm render

forwardRef chấp nhận một hàm render làm đối số. React gọi hàm này với propsref:

const MyInput = forwardRef(function MyInput(props, ref) {
return (
<label>
{props.label}
<input ref={ref} />
</label>
);
});

Tham số

  • props: Các props được truyền bởi component cha.

  • ref: Thuộc tính ref được truyền bởi component cha. ref có thể là một đối tượng hoặc một hàm. Nếu component cha không truyền ref, nó sẽ là null. Bạn nên truyền ref bạn nhận được cho một component khác hoặc truyền nó cho useImperativeHandle.

Giá trị trả về

forwardRef trả về một React component mà bạn có thể render trong JSX. Không giống như các React component được định nghĩa là các hàm thuần túy, component được trả về bởi forwardRef có thể nhận một prop ref.


Cách sử dụng

Hiển thị một DOM node cho component cha

Theo mặc định, các DOM node của mỗi component là riêng tư. Tuy nhiên, đôi khi hữu ích khi hiển thị một DOM node cho component cha—ví dụ: để cho phép focus nó. Để chọn tham gia, hãy bọc định nghĩa component của bạn vào forwardRef():

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} />
</label>
);
});

Bạn sẽ nhận được một ref làm đối số thứ hai sau props. Truyền nó đến DOM node mà bạn muốn hiển thị:

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} ref={ref} />
</label>
);
});

Điều này cho phép component Form cha truy cập <input> DOM node được hiển thị bởi MyInput:

function Form() {
const ref = useRef(null);

function handleClick() {
ref.current.focus();
}

return (
<form>
<MyInput label="Enter your name:" ref={ref} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}

Component Form này truyền một ref cho MyInput. Component MyInput chuyển tiếp ref đó đến thẻ trình duyệt <input>. Do đó, component Form có thể truy cập DOM node <input> đó và gọi focus() trên nó.

Hãy nhớ rằng việc hiển thị một ref cho DOM node bên trong component của bạn sẽ khiến việc thay đổi các thành phần bên trong của component sau này trở nên khó khăn hơn. Bạn thường sẽ hiển thị các DOM node từ các component cấp thấp có thể tái sử dụng như nút hoặc đầu vào văn bản, nhưng bạn sẽ không làm điều đó cho các component cấp ứng dụng như hình đại diện hoặc nhận xét.

Ví dụ về chuyển tiếp ref

Example 1 of 2:
Tập trung vào một đầu vào văn bản

Nhấp vào nút sẽ tập trung vào đầu vào. Component Form xác định một ref và truyền nó cho component MyInput. Component MyInput chuyển tiếp ref đó đến trình duyệt <input>. Điều này cho phép component Form tập trung vào <input>.

import { useRef } from 'react';
import MyInput from './MyInput.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}


Chuyển tiếp một ref qua nhiều component

Thay vì chuyển tiếp một ref đến một DOM node, bạn có thể chuyển tiếp nó đến component của riêng bạn như MyInput:

const FormField = forwardRef(function FormField(props, ref) {
// ...
return (
<>
<MyInput ref={ref} />
...
</>
);
});

Nếu component MyInput đó chuyển tiếp một ref đến <input> của nó, một ref đến FormField sẽ cung cấp cho bạn <input> đó:

function Form() {
const ref = useRef(null);

function handleClick() {
ref.current.focus();
}

return (
<form>
<FormField label="Enter your name:" ref={ref} isRequired={true} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}

Component Form xác định một ref và truyền nó cho FormField. Component FormField chuyển tiếp ref đó đến MyInput, và MyInput chuyển tiếp nó đến một DOM node <input> của trình duyệt. Đây là cách Form truy cập DOM node đó.

import { useRef } from 'react';
import FormField from './FormField.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <FormField label="Enter your name:" ref={ref} isRequired={true} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}


Hiển thị một imperative handle thay vì một DOM node

Thay vì hiển thị toàn bộ DOM node, bạn có thể hiển thị một đối tượng tùy chỉnh, được gọi là imperative handle, với một tập hợp các phương thức bị ràng buộc hơn. Để làm điều này, bạn cần xác định một ref riêng biệt để giữ DOM node:

const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);

// ...

return <input {...props} ref={inputRef} />;
});

Truyền ref bạn nhận được cho useImperativeHandle và chỉ định giá trị bạn muốn hiển thị cho ref:

import { forwardRef, useRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);

useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);

return <input {...props} ref={inputRef} />;
});

Nếu một số component nhận được một ref đến MyInput, nó sẽ chỉ nhận được đối tượng { focus, scrollIntoView } của bạn thay vì DOM node. Điều này cho phép bạn giới hạn thông tin bạn hiển thị về DOM node của mình ở mức tối thiểu.

import { useRef } from 'react';
import MyInput from './MyInput.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
    // This won't work because the DOM node isn't exposed:
    // ref.current.style.opacity = 0.5;
  }

  return (
    <form>
      <MyInput placeholder="Enter your name" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

Đọc thêm về sử dụng imperative handle.

Chú Ý

Không lạm dụng refs. Bạn chỉ nên sử dụng refs cho các hành vi bắt buộc mà bạn không thể thể hiện dưới dạng props: ví dụ: cuộn đến một node, tập trung vào một node, kích hoạt một hoạt ảnh, chọn văn bản, v.v.

Nếu bạn có thể thể hiện một cái gì đó như một prop, bạn không nên sử dụng ref. Ví dụ: thay vì hiển thị một imperative handle như { open, close } từ một component Modal, tốt hơn là lấy isOpen làm một prop như <Modal isOpen={isOpen} />. Effects có thể giúp bạn hiển thị các hành vi bắt buộc thông qua props.


Khắc phục sự cố

Component của tôi được bọc trong forwardRef, nhưng ref đến nó luôn là null

Điều này thường có nghĩa là bạn đã quên sử dụng ref mà bạn đã nhận được.

Ví dụ: component này không làm gì với ref của nó:

const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input />
</label>
);
});

Để khắc phục, hãy truyền ref xuống một DOM node hoặc một component khác có thể chấp nhận một ref:

const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input ref={ref} />
</label>
);
});

ref đến MyInput cũng có thể là null nếu một số logic là có điều kiện:

const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
{showInput && <input ref={ref} />}
</label>
);
});

Nếu showInputfalse, thì ref sẽ không được chuyển tiếp đến bất kỳ node nào và một ref đến MyInput sẽ vẫn trống. Điều này đặc biệt dễ bỏ lỡ nếu điều kiện được ẩn bên trong một component khác, như Panel trong ví dụ này:

const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
<Panel isExpanded={showInput}>
<input ref={ref} />
</Panel>
</label>
);
});