Ref ব্যবহার করে DOM ম্যানিপুলেশন
React স্বয়ংক্রিয়ভাবে DOM আপডেট করে যেন এটি আপনার রেন্ডার আউটপুটের সাথে মিলে যায়, তাই আপনার কম্পোনেন্টের প্রায় সময়ই দরকার হবে না এটা পরিবর্তন করার। তবে, মাঝে মাঝে আপনার React পরিচালিত DOM elements এর প্রয়োজন হতে পারে—উদাহরণস্বরূপ, একটি node কে ফোকাস করতে, স্ক্রল করতে, অথবা এর আকার এবং অবস্থান পরিমাপ করতে। React এ এই ধরণের কাজ করার জন্য কোনো বিল্ট-ইন উপায় নেই, তাই আপনার DOM node এর ref প্রয়োজন হবে।
যা যা আপনি শিখবেন
- কীভাবে React এর পরিচালিত একটি DOM নোড
ref
এট্রিবিউট ব্যবহার করে অ্যাক্সেস করবেন - কীভাবে
ref
JSX এট্রিবিউটuseRef
হুকের সাথে সম্পর্কিত। - কীভাবে অন্য একটি কম্পোনেন্টের DOM নোড অ্যাক্সেস করবেন
- কোন কোন ক্ষেত্রে React এর পরিচালিত একটি DOM নোড পরিবর্তন করা নিরাপদ
একটা নোডকে ref পর্যন্ত নিয়ে যাওয়া
React এর পরিচালিত একটি DOM নোড অ্যাক্সেস করতে, প্রথমে, useRef
হুকটি ইমপোর্ট করুনঃ
import { useRef } from 'react';
এর পর, একে ব্যবহার করে আপনার কম্পোনেন্টের মধ্যে একটি ref ডিক্লেয়ার করেনঃ
const myRef = useRef(null);
সবশেষে, যেই JSX ট্যাগের জন্য আপনি DOM নোড চাচ্ছেন, সেটায় একে ref
এট্রিবিউট হিসেবে পাস করে দিনঃ
<div ref={myRef}>
useRef
হুক একটি অবজেক্ট রিটার্ন করে যার একটি মাত্র প্রপার্টি থাকে current
নামে। প্রাথমিকভাবে, myRef.current
হবে null
। যখন React এই <div>
এর জন্য একটি DOM node তৈরি করে, React এই নোডের একটি রেফারেন্স myRef.current
-এ রাখবে। তারপর আপনি আপনার event handlers থেকে এই DOM node এ অ্যাক্সেস করতে পারেন এবং এর উপর ডিফাইন করা বিল্ট-ইন browser APIs ব্যবহার করতে পারেন।
// আপনি যেকোন ব্রাউজার API ব্যবহার করতে পারেন। উদাহরণস্বরূপঃ
myRef.current.scrollIntoView();
উদাহরণঃ একটা টেক্সট ইনপুটে ফোকাস করা
এই উদাহরণে, বাটনে ক্লিক করলে ইনপুট ফোকাস হবেঃ
import { useRef } from 'react'; export default function Form() { const inputRef = useRef(null); function handleClick() { inputRef.current.focus(); } return ( <> <input ref={inputRef} /> <button onClick={handleClick}> Focus the input </button> </> ); }
এটা করার জন্যঃ
useRef
হুক ব্যবহার করেinputRef
ডিক্লেয়ার করুন।<input ref={inputRef}>
হিসেবে একে পাস করে দিন। এটা React কে বলবে যেন এই<input>
এর DOM নোডinputRef.current
এর মধ্যে রাখা হয়।handleClick
ফাংশনে,inputRef.current
থেকে ইনপুট DOM নোড রিড করুন এবংinputRef.current.focus()
দিয়ে এর উপরfocus()
কল করুন।handleClick
ইভেন্ট হ্যান্ডলারটিonClick
এর সাহায্যে<button>
এ পাঠিয়ে দিন।
যদিও DOM ম্যানিপুলেশন এর জন্যই refs এর ব্যবহার সবচেয়ে বেশি হয়, useRef
হুক React এর বাইরে অন্যান্য জিনিস সংরক্ষণ করার জন্য ব্যবহৃত হতে পারে, যেমন টাইমার ID। State এর মতোই, রেন্ডারে মধ্যবর্তী সময়ে ref ঠিকঠাক থাকে। Refs হল state variables এর মত যেগুলি সেট করার সময় re-render ট্রিগার হয় না। ref সম্বন্ধে পড়ুন ref এর সাহায্যে ভ্যালু রেফারেন্সিং অংশে।
উদাহরণঃ কোন এলিমেন্ট পর্যন্ত স্ক্রল করা
আপনি একটি কম্পোনেন্টে একাধিক ref রাখতে পারেন। এই উদাহরণে, তিনটি চিত্রের একটি ক্যারাসেল আছে। প্রতিটি বাটন ব্রাউজারে ওই ছবির সাথে সম্পর্কিত DOM নোডে scrollIntoView()
মেথড কল করে ছবিটিকে মাঝামাঝি আনে।
import { useRef } from 'react'; export default function CatFriends() { const firstCatRef = useRef(null); const secondCatRef = useRef(null); const thirdCatRef = useRef(null); function handleScrollToFirstCat() { firstCatRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); } function handleScrollToSecondCat() { secondCatRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); } function handleScrollToThirdCat() { thirdCatRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); } return ( <> <nav> <button onClick={handleScrollToFirstCat}> Tom </button> <button onClick={handleScrollToSecondCat}> Maru </button> <button onClick={handleScrollToThirdCat}> Jellylorum </button> </nav> <div> <ul> <li> <img src="https://placekitten.com/g/200/200" alt="Tom" ref={firstCatRef} /> </li> <li> <img src="https://placekitten.com/g/300/200" alt="Maru" ref={secondCatRef} /> </li> <li> <img src="https://placekitten.com/g/250/200" alt="Jellylorum" ref={thirdCatRef} /> </li> </ul> </div> </> ); }
গভীরভাবে জানুন
উপরের উদাহরণগুলিতে, ref এর একটি পূর্বনির্ধারিত সংখ্যা রয়েছে। যদিও, মাঝে মাঝে আপনার তালিকার প্রতিটি আইটেমের জন্য একটি ref প্রয়োজন হতে পারে, এবং আপনি জানবেন না আপনার কতগুলো থাকবে। এরকম কিছু কাজ করবে নাঃ
<ul>
{items.map((item) => {
// কাজ করে না!
const ref = useRef(null);
return <li ref={ref} />;
})}
</ul>
এর কারণ হুক শুধুমাত্র আপনার কম্পোনেন্টের শীর্ষ-স্তরে ডাকতে হবে। আপনি একটি লুপে, একটি কন্ডিশনে, বা একটি map()
কলের ভিতরে useRef
ডাকতে পারবেন না।
একে এডিয়ে যাবার একটি সম্ভাব্য উপায় হল প্যারেন্ট এলিমেন্টে একটা মাত্র ref নিয়ে যাওয়া এবং তারপরে DOM ম্যানিপুলেশন পদ্ধতিগুলি যেমন querySelectorAll
ব্যবহার করে এটি থেকে individual child node “খুঁজে” বের করা। যদিও, এটি অত্যন্ত নাজুক এবং যদি আপনার DOM কাঠামো পরিবর্তন হয় তবে এটি ভেঙে যেতে পারে।
অন্য একটি সমাধান হলো ref
এট্রিবিউটে একটি ফাংশন পাস করা। এটি ref
callback. নামে পরিচিত। React যখন ref সেট করার সময় হবে তখন এটি আপনার ref callback কে DOM নোড দিয়ে কল করবে, এবং যখন এটি সাফ করার সময় হবে তখন null
দিয়ে ডাকবে। এটি আপনাকে আপনার নিজের একটি array বা একটি Map বজায় রাখতে দেয়, এবং এর মাধ্যমে আপনি তার ইনডেক্স বা কোন ধরণের ID দ্বারা যেকোনো ref অ্যাক্সেস করতে পারেন।
এই উদাহরণটি দেখায় যে আপনি এই পদ্ধতিটি কীভাবে ব্যবহার করে একটি দীর্ঘ তালিকায় যেকোনো নোডে স্ক্রল করতে পারেনঃ
import { useRef } from 'react'; export default function CatFriends() { const itemsRef = useRef(null); function scrollToId(itemId) { const map = getMap(); const node = map.get(itemId); node.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); } function getMap() { if (!itemsRef.current) { // প্রথম ব্যবহারে ম্যাপ ইনিশিয়ালাইজ করুন itemsRef.current = new Map(); } return itemsRef.current; } return ( <> <nav> <button onClick={() => scrollToId(0)}> Tom </button> <button onClick={() => scrollToId(5)}> Maru </button> <button onClick={() => scrollToId(9)}> Jellylorum </button> </nav> <div> <ul> {catList.map(cat => ( <li key={cat.id} ref={(node) => { const map = getMap(); if (node) { map.set(cat.id, node); } else { map.delete(cat.id); } }} > <img src={cat.imageUrl} alt={'Cat #' + cat.id} /> </li> ))} </ul> </div> </> ); } const catList = []; for (let i = 0; i < 10; i++) { catList.push({ id: i, imageUrl: 'https://placekitten.com/250/200?image=' + i }); }
এউ উদাহরণে, itemsRef
একটা DOM নোডও রাখে না। বরং এটা আইটেম ID থেকে DOM নোডের একটা ম্যাপ রাখে। তালিকার প্রতিটি আইটেমে ref
callback ম্যাপের আপডেটের বিষয়টা ঠিক রাখে।
<li
key={cat.id}
ref={node => {
const map = getMap();
if (node) {
// ম্যাপে যোগ করুন
map.set(cat.id, node);
} else {
// ম্যাপ থেকে সরিয়ে ফেলুন
map.delete(cat.id);
}
}}
>
এটা আপনাকে ম্যাপ থেকে DOM নোড পৃথকভাবে রিড করতে দেয়।
অন্য একটি কম্পোনেন্টের DOM নোড অ্যাক্সেস করা
আপনি যখন এমন একটা বিল্ট-ইন কম্পোনেন্টে রেফ বসান যা <input />
এর মত একটি ব্রাউজার এলিমেন্ট আউটপুট হিসেবে দেয়, React সেই ref এর current
হিসেবে প্রপার্টি সম্পর্কিত DOM নোড (যেমন ব্রাউজারের প্রকৃত <input />
) সেট করে দেবে।
তবে, আপনি যদি আপনার নিজের কম্পোনেন্টে একটা ref বসাতে চান, যেমন <MyInput />
, তাহলে স্বাভাবিকভাবে আপনি null
পাবেন। এখানে বিষয়টা দেখায় এমন একটি উদাহরণ দেখানো হল। খেয়াল করুন বাটনে ক্লিক করলে ইনপুটে ফোকাস হয় না।
import { useRef } from 'react'; function MyInput(props) { return <input {...props} />; } export default function MyForm() { const inputRef = useRef(null); function handleClick() { inputRef.current.focus(); } return ( <> <MyInput ref={inputRef} /> <button onClick={handleClick}> Focus the input </button> </> ); }
আপনি যেন ঝামেলাটা খেয়াল করেন, সেজন্য React কনসোলে একটি এরর দেখিয়ে দেয়ঃ
এটি ঘটে কারণ স্বাভাবিকভাবে React কম্পোনেন্টকে অন্যান্য কম্পোনেন্টের DOM নোড অ্যাক্সেস করতে দেয় না। তার নিজের children দের জন্যও নয়! এটি ইচ্ছাকৃত। Ref এক ধরণের escape hatch যা খুব কম ব্যবহার করা উচিত। ম্যানুয়ালি অন্য কম্পোনেন্টের DOM নোড পরিবর্তন করা আপনার কোডকে আরও নাজুক বানিয়ে ফেলে।
এর পরিবর্তে, যে কম্পোনেন্টগুলি তাদের DOM নোড উন্মুক্ত করতে চায় তাদেরকে এই আচরণ আয়ত্ব করে নিতে হবে। একটি কম্পোনেন্ট নির্দিষ্ট করতে পারে যে এটি তার ref তার একটি সন্তানের কাছে “ফরোয়ার্ড” করে। এটা কিভাবে MyInput forwardRef API ব্যবহার করতে পারে তা দেখানো হলো:
const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
এটা কাজ করে এই ভাবেঃ
<MyInput ref={inputRef} />
React কে বলে corresponding DOM নোডinputRef.current
এর মধ্যে রাখতে। কিন্তু, এই সিদ্ধান্তটাMyInput
কম্পোনেন্টের উপর নির্ভর করে যে সে এটা করবে কি না—স্বাভাবিকভাবে সে এটা করে না।MyInput
কম্পোনেন্টটাforwardRef
ব্যবহার করে ডিক্লেয়ার করা হয়। এটা উপরেরinputRef
কে দ্বিতীয়ref
আর্গুমেন্ট হিসেবে নেওয়ার সিদ্ধান্ত নেয়, যাprops
এর পরে ডিক্লেয়ার করা হয়।MyInput
যেইref
টা পায় সেটা নিজেই এর ভিতরকার<input>
এ পাস করে দেয়।
এখন বাটন ক্লিক করে ইনপুট ফোকাস হচ্ছে ঠিকঠাকভাবেঃ
import { forwardRef, useRef } from 'react'; const MyInput = forwardRef((props, ref) => { return <input {...props} ref={ref} />; }); export default function Form() { const inputRef = useRef(null); function handleClick() { inputRef.current.focus(); } return ( <> <MyInput ref={inputRef} /> <button onClick={handleClick}> Focus the input </button> </> ); }
সাধারণত ডিজাইন সিস্টেমে, বাটন, ইনপুট এবং এরকম কিছু অন্যান্য নিম্ন-স্তরের কম্পোনেন্টগুলির জন্য তাদের ref তাদের DOM নোডে ফরওয়ার্ড করা একটি সাধারণ প্যাটার্ন। অন্যদিকে, ফরম, তালিকা, বা পেইজের সেকশনের মতো উচ্চ-স্তরের কম্পোনেন্টগুলি সাধারণত DOM নোড প্রকাশ করবে না যাতে DOM কাঠামোতে আকস্মিক নির্ভরতা কমে যায়।
গভীরভাবে জানুন
উপরের উদাহরণে, MyInput
মূল DOM ইনপুট এলিমেন্টটি প্রকাশ করে। এটি প্যারেন্ট কম্পোনেন্টকে এটির উপর focus()
কল করার সুযোগ দেয়। যদিও, এটি প্যারেন্ট কম্পোনেন্টকে অন্য একটা কাজ সুযোগ করে দেয় - উদাহরণস্বরূপ, এর CSS স্টাইল পরিবর্তন করা। হঠাৎ হঠাৎ, আপনি হয়তো exposed functionality সীমাবদ্ধ করতে চাইবেন। আপনি এটি useImperativeHandle
এর সাহায্যে করতে পারেনঃ
import { forwardRef, useRef, useImperativeHandle } from 'react'; const MyInput = forwardRef((props, ref) => { const realInputRef = useRef(null); useImperativeHandle(ref, () => ({ // শুধুমাত্র ফোকাস উন্মুক্ত করুন আর কিছু না focus() { realInputRef.current.focus(); }, })); return <input {...props} ref={realInputRef} />; }); export default function Form() { const inputRef = useRef(null); function handleClick() { inputRef.current.focus(); } return ( <> <MyInput ref={inputRef} /> <button onClick={handleClick}> Focus the input </button> </> ); }
এখানে, MyInput
এর মধ্যে realInputRef
আসল ইনপুট DOM নোডটি ধারণ করে। যদিও, useImperativeHandle
React-কে বলে যেন প্যারেন্ট কম্পোনেন্টের জন্য ref এর মান হিসাবে আপনার নিজের বিশেষ একটা অবজেক্ট সরবরাহ করবে। তাই Form
কম্পোনেন্টের ভেতরে inputRef.current
শুধু মাত্র focus
মেথডটি আছে। এই ক্ষেত্রে, ref “handle” DOM নোড নয়, বরং useImperativeHandle
কলের ভেতরে আপনি যে কাস্টম অবজেক্ট তৈরি করেন সেটা।
When React attaches the refs
React এ, প্রতিটি আপডেট দুটি পর্যায়ে ভাগ করা হয়ঃ
- render এর সময়, React আপনার কম্পোনেন্টগুলি কল করে যেন পর্দায় কী থাকা উচিত তা নির্ধারণ করতে পারে।
- commit এর সময়, React DOM এ পরিবর্তনগুলি প্রয়োগ করে।
সাধারণত, আপনি রেন্ডারিং এর সময় ref গুলিতে অ্যাক্সেস করতে চান না। DOM নোডগুলি ধারণ করা ref এর জন্য এটিও প্রযোজ্য। প্রথম রেন্ডারের সময়, DOM নোডগুলি এখনও তৈরি হয়নি, সুতরাং ref.current
হবে null
। আর আপডেট রেন্ডারিং এর সময়, DOM নোডগুলি এখনও আপডেট হয়নি। সুতরাং এখনো তাদের read করার সময় হয় নাই।
React কমিটের সময় ref.current
সেট করে। DOM আপডেট করার আগে, React প্রভাবিত ref.current
মানগুলিকে null
সেট করে। DOM আপডেট করার পরে, React তা অবিলম্বে সম্পর্কিত DOM নোডগুলিতে সেট করে।
সাধারণত, আপনি ইভেন্ট হ্যান্ডলারগুলি থেকে ref গুলি অ্যাক্সেস করবেন। আপনি যদি কোনও ref এর সাথে কিছু করতে চান, কিন্তু এটি করার জন্য কোনও নির্দিষ্ট ইভেন্ট না থাকে, আপনার একটি Effect প্রয়োজন হতে পারে। আমরা পরবর্তী সেকশন গুলিতে Effects সম্পর্কে আলোচনা করব।
গভীরভাবে জানুন
এরকম কোড বিবেচনা করুন, যা একটি নতুন todo যোগ করে এবং স্ক্রিনটি তালিকার শেষ চাইল্ডের দিকে স্ক্রল করে। লক্ষ্য করুন কিভাবে, কিছু কারণে, এটি সবসময় শেষে যোগ করা todo এর ঠিক আগের একটিতে স্ক্রল করেঃ
import { useState, useRef } from 'react'; export default function TodoList() { const listRef = useRef(null); const [text, setText] = useState(''); const [todos, setTodos] = useState( initialTodos ); function handleAdd() { const newTodo = { id: nextId++, text: text }; setText(''); setTodos([ ...todos, newTodo]); listRef.current.lastChild.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } return ( <> <button onClick={handleAdd}> Add </button> <input value={text} onChange={e => setText(e.target.value)} /> <ul ref={listRef}> {todos.map(todo => ( <li key={todo.id}>{todo.text}</li> ))} </ul> </> ); } let nextId = 0; let initialTodos = []; for (let i = 0; i < 20; i++) { initialTodos.push({ id: nextId++, text: 'Todo #' + (i + 1) }); }
এই দুটি লাইনে সমস্যাটি আছেঃ
setTodos([ ...todos, newTodo]);
listRef.current.lastChild.scrollIntoView();
React এ, state আপডেট কিউ করে রাখা হয়। সাধারণত, আপনি এইটাই চাইবেন। তবে, এখানে এটা সমস্যা করে কারণ setTodos
DOM কে সাথে সাথে আপডেট করে না। সুতরাং যখন আপনি স্ক্রল করে তালিকের শেষ এলিমেন্টে যাবেন, তখনো todo যোগ করা হয় নাই। এই কারণে স্ক্রলিং সব সময়ে এক আইটেমের হিসেবে “পিছিয়ে থাকে।“
এই সমস্যাটা সমাধানের জন্য, আপনি React কে DOM সিঙ্ক্রোনাসভাবে আপডেট (“flush”) করতে বাধ্য করতে পারেন। এটা করার জন্য react-dom
থেকে flushSync
ইমপোর্ট করুন এবং state আপডেটকে ঘিরে ফেলুন একটা flushSync
কলের মধ্যেঃ
flushSync(() => {
setTodos([ ...todos, newTodo]);
});
listRef.current.lastChild.scrollIntoView();
এটা React কে নির্দেশ করবে যেন flushSync
দিয়ে ঘেরা কোড চলার সাথে সাথে সিঙ্ক্রোনাস ভাবে DOM আপডেট হয়ে যায়। ফলাফলস্বরূপ, আপনি যতক্ষণে শেষ TODO তে স্ক্রল করে যাবেন তার আগেই এটা DOM এর মধ্যে থাকবে।
import { useState, useRef } from 'react'; import { flushSync } from 'react-dom'; export default function TodoList() { const listRef = useRef(null); const [text, setText] = useState(''); const [todos, setTodos] = useState( initialTodos ); function handleAdd() { const newTodo = { id: nextId++, text: text }; flushSync(() => { setText(''); setTodos([ ...todos, newTodo]); }); listRef.current.lastChild.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } return ( <> <button onClick={handleAdd}> Add </button> <input value={text} onChange={e => setText(e.target.value)} /> <ul ref={listRef}> {todos.map(todo => ( <li key={todo.id}>{todo.text}</li> ))} </ul> </> ); } let nextId = 0; let initialTodos = []; for (let i = 0; i < 20; i++) { initialTodos.push({ id: nextId++, text: 'Todo #' + (i + 1) }); }
ref ব্যবহার করে DOM ম্যানিপুলেশনের ক্ষেত্রে যা যা মেনে চলা উচিত
Ref হচ্ছে এক ধরণের escape hatch। আপনার শুধুমাত্র “React এর বাইরে পদক্ষেপ নিতে” হলে এর ব্যবহার করা উচিত। এই ধরনের সাধারণ উদাহরণগুলির মধ্যে রয়েছে ফোকাস করা, স্ক্রোল অবস্থান বা ব্রাউজার API গুলি কল করা যা React প্রকাশ করে না।
আপনি যদি ফোকাসিং এবং স্ক্রোলিং এর মতো অবিনাশী কাজগুলিতে স্থাপিত থাকেন, তবে আপনার কোনও সমস্যা হওয়ার কথা নয়। যদিও, আপনি যদি ম্যানুয়ালি DOM পরিবর্তন করার চেষ্টা করেন, তবে আপনি React এর করা পরিবর্তনের সাথে বিরোধিতা ঘটানোর ঝুঁকি নিতে পারেন।
এই সমস্যাটা ব্যাখ্যা করতে, এই উদাহরণে একটা ওয়েলকাম মেসেজ এবং দুটি বাটন আছে। প্রথম বাটনটি conditional rendering এবং state ব্যবহার করে এর উপস্থিতি বদল করে, যেমন আপনি স্বাভাবিকভাবে React এ করেন। দ্বিতীয় বাটনটি remove()
DOM API ব্যবহার করে একে জোর করে DOM থকে সরিয়ে React এর নিয়ন্ত্রণের বাইরে পাঠিয়ে দেয়।
কয়েকবার “Toggle with setState” চাপার চেষ্টা করেন। মেসেজটা হারিয়ে যাবার কথা আবার আসার কথা। এরপরে “Remove from the DOM” এ চাপ দিন। এটা একে জোর করে সরিয়ে ফেলবে। সবশেষে, “Toggle with setState” চাপ দিনঃ
import { useState, useRef } from 'react'; export default function Counter() { const [show, setShow] = useState(true); const ref = useRef(null); return ( <div> <button onClick={() => { setShow(!show); }}> Toggle with setState </button> <button onClick={() => { ref.current.remove(); }}> Remove from the DOM </button> {show && <p ref={ref}>Hello world</p>} </div> ); }
আপনি নিজে DOM এলিমেন্ট সরিয়ে ফেলবার পরে, setState
ব্যবহার করে একে আবার দেখানোর চেষ্টা করলে সিস্টেম ক্র্যাশ করবে। এর কারণ আপনি DOM বদলে ফেলেছেন, এবং React জানে না এর পরে একে কীভাবে পরিচালনা করবে।
React দিয়ে পরিচালিত DOM নোড পরিবর্তন এড়িয়ে চলবেন। যেসব এলিমেন্ট React পরিচালনা করে সেগুলোর পরিবর্তন, চাইল্ড যোগ করা বা চাইল্ড বাদ দেয়ার কারণে ভিজ্যুয়াল ফলাফলে অসামাঞ্জস্য দেখা যেতে পারে বা উপরের মত ক্র্যাশ করতে পারে।
তবে, এর মানে এটা না যে আপনি এটা একদমই করতে পারবেন না। এর জন্য দরকার সতর্কতা। আপনি নিরাপদভাবে DOM এর সেই অংশগুলো পরিবর্তন করতে পারবেন যেগুলো React এর আপডেট করার কোন কারণ নেই। উদাহরণস্বরূপ, যদি JSX এ কোন <div>
সবসময় খালি থাকে, এর চিলড্রেন তালিকায় হাত দেয়ার কোন কারণ থাকবে না React এর। সুতরাং, সেখানে নিজ থেকে এলিমেন্ট যুক্ত করা বা বাদ দেওয়াটা নিরাপদ।
পুনরালোচনা
- Ref একটি সাধারণ ধারণা, কিন্তু বেশিরভাগ সময় আপনি DOM এলিমেন্ট হোল্ড করার জন্য এর ব্যবহার করবেন।
- আপনি
<div ref={myRef}>
পাস করার মাধ্যমে React কে নির্দেশ করবেন যেনmyRef.current
এর মধ্যে একটি DOM নোড ঢুকানো হয়। - সাধারণত, আপনি ref ব্যবহার করবেন এমন কাজ করার জন্য যা ধ্বংসাত্মক না। যেমন ফোকাস করা, স্ক্রল করা বা DOM এলিমেন্টের measure করা।
- একটা কম্পোনেন্ট এর DOM নোড স্বাভাবিকভাবে উন্মুক্ত করে না। আপনি
forwardRef
ব্যবহার করার মাধ্যমে এবং একটা নির্দিষ্ট নোডে দ্বিতীয়ref
আর্গুমেন্ট পাঠিয়ে দেবার মাধ্যমে DOM নোড উন্মুক্ত করার সিদ্ধান্ত নিতে পারেন। - React পরিচালনা করে এমন DOM নোড পরিবর্তন করা এড়িয়ে চলুন।
- আপনি যদি React এর পরিচালিত DOM নোড পরিবর্তন করে, তাহলে এমন অংশে পরিবর্তনটা আনেন যেটা React এর আপডেট করার কোন কারণ নেই।
চ্যালেঞ্জ 1 / 4: ভিডিও চালু এবং বন্ধ করুন
এই উদাহরণে, ভিডিও চালু এবং বন্ধ থাকা অবস্থার সুইচ করার জন্য বাটন একটি state ভ্যারিয়েবল পরিবর্তন করে। তবে, ভিডিও আসলেই বন্ধ বা চালু করার জন্য, state ভ্যারিয়েবল পরিবর্তন করা যথেষ্ট না। আপনাকে <video>
এর DOM এলিমেন্টে play()
এবং pause()
কল করতে হবে। এর সাথে একটি ref যোগ করুন, এবং নিশ্চিত করুন যে বাটনটা কাজ করছে।
import { useState, useRef } from 'react'; export default function VideoPlayer() { const [isPlaying, setIsPlaying] = useState(false); function handleClick() { const nextIsPlaying = !isPlaying; setIsPlaying(nextIsPlaying); } return ( <> <button onClick={handleClick}> {isPlaying ? 'Pause' : 'Play'} </button> <video width="250"> <source src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4" type="video/mp4" /> </video> </> ) }
একটা অতিরিক্ত চ্যালেঞ্জের জন্য, যদি ব্যবহারকারী ভিডিওতে রাইট ক্লিক করে বিল্ট-ইন ব্রাউজার মিডিয়া কন্ট্রোল ব্যবহার করে ভিডিও চালু করে দেয়, সেটার সাথে “Play” বাটন sync রাখুন। আপনি ভিডিওর onPlay
এবং onPause
listen করতে চাইতে পারেন।