forwardRef
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) {
// ...
});
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 props
và ref
:
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ínhref
đượ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ềnref
bạn nhận được cho một component khác hoặc truyền nó chouseImperativeHandle
.
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.
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.
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 showInput
là false
, 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>
);
});