forwardRef
forwardRef
আপনার কম্পোনেন্টকে একটি DOM নোড একটি প্যারেন্ট কম্পোনেন্টে একটি ref সহ এক্সপোজ করার সুযোগ দেয়।
const SomeComponent = forwardRef(render)
রেফারেন্স
forwardRef(render)
forwardRef()
কল করুন যেন আপনার কম্পোনেন্ট একটি ref রিসিভ করতে পারে এবং একটি চাইল্ড কম্পোনেন্টে ফরোয়ার্ড করতে পারেঃ
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
// ...
});
প্যারামিটার
render
: আপনার কম্পোনেন্টের রেন্ডার ফাংশন। আপনার কম্পোনেন্ট তার প্যারেন্ট থেকে যে প্রপ এবংref
পায় সেটা নিয়ে React এই ফাংশনে কল করে। আপনি যেই JSX রিটার্ন করবেন সেটা হবে আপনার কম্পোনেন্টের আউটপুট।
রিটার্ন
forwardRef
এমন একটা React কম্পোনেন্ট রিটার্ন করে যেটা আপনি JSX এ রেন্ডার করতে পারেন। সোজাসাপ্টা ফাংশন হিসেবে ডিফাইন করা React কম্পোনেন্টের সাথে এর অমিল এখানেই যে, forwardRef
দ্বারা রিটার্ন হওয়া কম্পোনেন্ট একটা ref
প্রপ রিসিভও করতে পারে।
সতর্কতা
- Strict Mode এ, React আপনাকে accidental impurities খুঁজে বের করতে সাহায্য করার জন্য আপনার রেন্ডার ফাংশন দুবার কল করবে। এটা development-only আচরণ এবং production এ কোন প্রভাব ফেলবে না। যদি আপনার রেন্ডার ফাংশন pure হয় (যেমন এর হওয়া উচিত), এটা আপনার কম্পোনেন্টের লজিকে কোন প্রভাব ফেলবার কথা না। দুটি কলের একটির ফলাফলকে আমলে আনা হবে না।
render
ফাংশন
forwardRef
একটি রেন্ডার ফাংশনকে একটি আর্গুমেন্ট হিসেবে গ্রহণ করে। React এই ফাংশনে props
এবং ref
সহ কল করেঃ
const MyInput = forwardRef(function MyInput(props, ref) {
return (
<label>
{props.label}
<input ref={ref} />
</label>
);
});
প্যারামিটার
-
props
: প্যারেন্ট কম্পোনেন্ট যে প্রপ পাস করে। -
ref
: প্যারেন্ট কম্পোনেন্টের পাস করাref
এট্রিবিউট। এইref
হতে পারে অবজেক্ট বা ফাংশন। যদি প্যারেন্ট কম্পোনেন্ট কোন ref পাস না করে থাকে, এটাnull
হবে। আপনার রিসিভ করাref
অন্য একটি কম্পোনেন্টে পাস করা উচিত, অথবাuseImperativeHandle
এ পাস করা উচিত।
রিটার্ন
forwardRef
একটি React কম্পোনেন্ট রিটার্ন করে যেটা আপনি JSX এ রেন্ডার করতে পারবেন। Plain ফাংশন হিসেবে সংজ্ঞায়িত React কম্পোনেন্টের সাথে এর অমিল এখানেই যে, forwardRef
থেকে রিটার্ন করা কম্পোনেন্ট একটি ref
প্রপ নিতে পারে।
ব্যবহার
প্যারেন্ট কম্পোনেন্টে একটি DOM নোড এক্সপোজ করা
ডিফল্ট ভাবে, প্রতিটা কম্পোনেন্টের DOM নোড প্রাইভেট। তবে, কখনো কখনো প্যারেন্টের দিকে একটা DOM নোড এক্সপোজ করা কাজে লাগতে পারে—যেমন, একে ফোকাসের সুযোগ দেবার জন্য। রটা করার জন্য আপনার কম্পোনেন্ট ডেফিনিশন forwardRef()
দিয়ে wrap করে ফেলুনঃ
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} />
</label>
);
});
আপনি props এর পর দ্বিতীয় আর্গুমেন্ট হিসেবে একটি ref রিসিভ করবেন। আপনি যেই DOM নোড এক্সপোজ করতে চান সেখানে এটি পাস করে দিনঃ
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} ref={ref} />
</label>
);
});
এইটা প্যারেন্ট Form
কম্পোনেন্টকে MyInput
এর কারণে এক্সপোজ হওয়া <input>
DOM নোড এক্সেসের সুযোগ দেয়।
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>
);
}
এই Form
কম্পোনেন্টটি MyInput
এ একটি ref পাস করে। MyInput
কম্পোনেন্ট এই ref কে <input>
ব্রাউজার ট্যাগে ফরোয়ার্ড করে দেয়। ফলে, Form
কম্পোনেন্ট ঐ <input>
DOM নোডে এক্সেস করতে পারে এবং এতে focus()
কল দিতে পারে।
মনে রাখবেন যে, আপনার কম্পোনেন্টের মধ্যে একটি ref এক্সপোজ করার ফোলে আপনার কম্পোনেন্টের ভেতরকার তথ্য পরিবর্তন করা কঠিন হয়ে পড়ে। আপনি বাটন বা টেক্সট ইনপুটের মত পুনরায় ব্যবহারযোগ্য লো-লেভেল কম্পোনেন্ট থেকেই সাধরণত DOM nodes এক্সপোজ করবেন, কিন্তু আপনি এপ্লিকেশন-লেভেল কম্পোনেন্ট যেমন avatar বা কমেন্ট এর জন্য এটি করবেন না।
উদাহরণ 1 / 2: একটি টেক্সট ইনপুটে ফোকাস করা
বাটনে ক্লিক করা হলে ইনপুট ফোকাস হবে। Form
কম্পোনেন্ট একটি ref ডিফাইন করে এবং MyInput
কম্পোনেন্টে পাস করে দেয়। MyInput
কম্পোনেন্ট সেই ref টি ব্রাউজার <input>
এ ফরোয়ার্ড করে দেয়। এর কারণে Form
কম্পোনেন্ট <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> ); }
একাধিক কম্পোনেন্টের মধ্য দিয়ে ref ফরোয়ার্ড
DOM নোডে ref
ফরোয়ার্ড করার বদলে, আপনি এটি আপনার নিজের কম্পোনেন্ট যেমন MyInput
এ ফরোয়ার্ড করতে পারেনঃ
const FormField = forwardRef(function FormField(props, ref) {
// ...
return (
<>
<MyInput ref={ref} />
...
</>
);
});
যদি MyInput
কম্পোনেন্ট তার <input>
এ একটি ref ফরোয়ার্ড করে, FormField
এর ref আপনাকে সেই <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>
);
}
Form
কম্পোনেন্ট একটি ref ডিফাইন করে এবং FormField
কম্পোনেন্টে পাস করে দেয়। FormField
কম্পোনেন্ট সেই ref টি MyInput
এ ফরোয়ার্ড করে, যা একে ব্রাউজার DOM নোড <input>
এ ফরোয়ার্ড করে। এই ভাবে Form
ঐ DOM নোড এক্সেস করে।
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> ); }
DOM নোডের বদলে একটি imperative handle এক্সপোজ করা
সম্পূর্ণ DOM নোড এক্সপোজ করবার বদলে, আপনি একটি কাস্টম অবজেক্ট এক্সপোজ করতে পারেন, যাকে imperative handle বলা হয়, যার সীমিত কিছু মেথড আছে। এটা করার জন্য, DOM নোড ধরে রাখতে আপনাকে একটি আলাদা ref ডিফাইন করতে হবেঃ
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
// ...
return <input {...props} ref={inputRef} />;
});
আপনার রিসিভ করা ref
, useImperativeHandle
এ পাস করে করে দিন এবং আপনি যেই ভ্যালু 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} />;
});
যদি কোন কম্পোনেন্ট MyInput
এর ref পায়, এটা DOM নোডের বদলে কেবল আপনার { focus, scrollIntoView }
অবজেক্ট রিসিভ করবে। এভাবে আপনি আপনার DOM নোডের তথ্যের সর্বনিম্ন পরিমাণ এক্সপোজ হবে।
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> ); }
Imperative handles সম্বন্ধে আরো পড়ুন।
ট্রাবলশ্যুট
আমার কম্পোনেন্ট forwardRef
এর মধ্যে wrap করা, কিন্তু এর ref
সবসময় null
সাধারণত এর অর্থ হল আপনি যেই ref
রিসিভ করেছেন, সেটা ব্যবহার করতে ভুলে গেছেন।
উদাহরণস্বরূপ, এই কম্পোনেন্ট এই ref
এর সাথে কিছু করে নাঃ
const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input />
</label>
);
});
এটা ঠিক করার জন্য, ref
কে নিচে DOM নোড বা অন্য এমন কোন কম্পোনেন্ট যা ref গ্রহণ করতে পারে সে পর্যন্ত নিয়ে যানঃ
const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input ref={ref} />
</label>
);
});
যদি কিছু লজিক কন্ডিশনাল হয় সেক্ষেত্রেও MyInput
এর ref
null
হতে পারেঃ
const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
{showInput && <input ref={ref} />}
</label>
);
});
যদি showInput
false
হয়, তাহলে ref কোন নোডে ফরোয়ার্ড হবে না, এবং MyInput
এর একটি ref ফাঁকা থাকবে। বিশেষ করে এই বিষয়টি সহজেই উপেক্ষিত হতে পারে যদি কন্ডিশন অন্য কোন কম্পোনেন্টের মধ্যে লুকিয়ে থাকে, যেমন এই উদাহরণে Panel
:
const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
<Panel isExpanded={showInput}>
<input ref={ref} />
</Panel>
</label>
);
});