State সংরক্ষণ এবং রিসেট করা
State কম্পোনেন্টের মধ্যে ভিন্ন রাখা হয়। React একটি UI ট্রি এর ভিতরে কোন State কোন কম্পোনেন্টের সঙ্গে সংযোগস্থলে পরিচয় রেখে থাকে। আপনি নিয়ন্ত্রণ করতে পারেন যখন অবস্থা সংরক্ষণ করতে হবে এবং যখন রি-রেন্ডার এর মধ্যে তা পুনরায় সেট করতে হবে।
যা যা আপনি শিখবেন
- React কীভাবে কম্পোনেন্ট স্ট্রাকচারগুলি “দেখে”
- React কোন সময়ে State সংরক্ষণ বা রিসেট করতে সিলেক্ট করে
- React এ কীভাবে কম্পোনেন্টের অবস্থা রিসেট করতে বাধ্য করা যায়
- React এ State সংরক্ষণ কি ভাবে প্রভাহিত হয় কীস (keys) এবং প্রকার (types) এর জন্য
UI ট্রি
ব্রাউজার অনেক ধরনের ট্রি কাঠামো ব্যবহার করে থাকে UI মডেল করার জন্য। DOM HTML উপাদানগুলি প্রতিষ্ঠা করে, CSSOM একইভাবে CSS এর জন্য করে। এখানে Accessibility tree নামক ট্রি আছে।
React সাধারণতঃ UI গড়ে তুলতে এবং পরিচালনা করতে ট্রি স্ট্রাকচার ব্যবহার করে। React JSX থেকে UI ট্রি তৈরি করে। তারপরে React DOM ব্রাউজারের DOM উপাদানগুলি আপডেট করে যাতে সেই UI ট্রির সাথে মিল খায়। (React Native এই ট্রিগুলি মোবাইল প্ল্যাটফর্মের উপাদানগুলির জন্য প্রতিষ্ঠান করে।)।
State ট্রির একটি অবস্থানের সাথে বাঁধা রয়েছে
যখন আপনি কোন কম্পোনেন্টকে স্টেট দিন, আপনি সম্প্রতি মনে করতে পারেন যে স্টেটটি কেবলমাত্র কম্পোনেন্টের “ভিতরে” বসে থাকে। কিন্তু স্টেটটি প্রদত্তক্ষেত্রে বাস করে যেমন রিয়েক্টে। রিয়েক্ট প্রতিটি স্টেট টুকরা সঠিক কম্পোনেন্ট সঙ্গে যুক্ত করে রাখে যেখানে ঐ কম্পোনেন্টটি UI ট্রি এর মধ্যে অবস্থান করে।
এখানে, কেবলমাত্র একটি <Counter />
JSX ট্যাগ আছে, কিন্তু এটি দুটি জায়গায় রেন্ডার হয়েছে:
import { useState } from 'react'; export default function App() { const counter = <Counter />; return ( <div> {counter} {counter} </div> ); } function Counter() { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return ( <div className={className} onPointerEnter={() => setHover(true)} onPointerLeave={() => setHover(false)} > <h1>{score}</h1> <button onClick={() => setScore(score + 1)}> Add one </button> </div> ); }
এখানে এগুলি কিভাবে ট্রি হিসেবে দেখায়:
এগুলি দুটি আলাদা কাউন্টার কারণ প্রতিটি নিজস্ব অবস্থানে ট্রি এর মধ্যে রেন্ডার করা হয়েছে। আপনাকে সাধারণতঃ রিয়েক্ট ব্যবহার করার সময় এই অবস্থানগুলি চিন্তা করতে হয় না, তবে কিভাবে এটি কার্য করে তা বোঝার জন্য এটা কার্যকর হতে পারে।
React-এ, প্রদর্শিত প্রতিটি কম্পোনেন্টের জন্য একটি সম্পূর্ণ আলাদা State আছে। উদাহরণস্বরূপ, যদি আপনি পাশাপাশি দুটি Counter
কম্পোনেন্টকে রেন্ডার করেন, তখন প্রতিটি কম্পোনেন্ট নিজস্ব, স্বতন্ত্র, score
এবং hover
states পাওয়া যাবে।
দুটি কাউন্টার একই সাথে ক্লিক করুন এবং দেখুন যে তারা একে অপরকে প্রভাবিত করে না:
import { useState } from 'react'; export default function App() { return ( <div> <Counter /> <Counter /> </div> ); } function Counter() { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return ( <div className={className} onPointerEnter={() => setHover(true)} onPointerLeave={() => setHover(false)} > <h1>{score}</h1> <button onClick={() => setScore(score + 1)}> Add one </button> </div> ); }
যেমন আপনি দেখতে পাচ্ছেন, একটি কাউন্টার আপডেট করা হলে, শুধুমাত্র সেই কম্পোনেন্টের স্টেটটি আপডেট হয়ছে:
React ততোক্ষণ State ধরে রাকবে যতক্ষণ একই পজিশনে একই কম্পোনেন্ট রেন্ডার করা হচ্ছে। এটা দেখতে, দুটি কাউন্টারের মান বাড়ানোর পর দ্বিতীয় কম্পোনেন্টটি সরানোর জন্য “দ্বিতীয় কাউন্টার রেন্ডার করুন” চেকবক্স আনচেক করুন, এবং তারপরে আবার সেইটি যুক্ত করতে আবার চেক করুন।
import { useState } from 'react'; export default function App() { const [showB, setShowB] = useState(true); return ( <div> <Counter /> {showB && <Counter />} <label> <input type="checkbox" checked={showB} onChange={e => { setShowB(e.target.checked) }} /> Render the second counter </label> </div> ); } function Counter() { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return ( <div className={className} onPointerEnter={() => setHover(true)} onPointerLeave={() => setHover(false)} > <h1>{score}</h1> <button onClick={() => setScore(score + 1)}> Add one </button> </div> ); }
লক্ষ্য করুন যে মুহূর্তে আপনি দ্বিতীয় কাউন্টার রেন্ডার করা বন্ধ করেন, তখন ঐ কাউন্টারের স্টেট সম্পূর্ণভাবে অদৃশ্য হয়ে যায়। এটা হওয়ার কারণ React কম্পোনেন্টটি সরানোর সময় তার স্টেট ধ্বংস করে।
যখন আপনি “দ্বিতীয় কাউন্টার রেন্ডার করুন” টিক চিহ্নতে টিক করেন, তখন একটি দ্বিতীয় Counter
এবং এর স্টেটটি শূন্য থেকে শুরু করা হয় (score = 0
) এবং এটি DOM-এ যুক্ত করা হয়।
React কম্পোনেন্টের State সংরক্ষণ করে রাখে ততক্ষণ পর্যন্ত যতোক্ষণ ঐ কম্পোনেন্টটি UI ট্রির মধ্যে নির্দিষ্ট অবস্থানে রেন্ডার হচ্ছে। যদি কোনও কম্পোনেন্ট সরানো হয় অথবা একই অবস্থানে একটি অন্য কম্পোনেন্ট রেন্ডার করা হয়, তবে React তার স্টেটটি বাতিল করে ফেলে।
একই কম্পোনেন্ট একই অবস্থানে থাকলে State সংরক্ষিত থাকে
এই উদাহরণে, দুটি আলাদা <Counter />
ট্যাগ আছে:
import { useState } from 'react'; export default function App() { const [isFancy, setIsFancy] = useState(false); return ( <div> {isFancy ? ( <Counter isFancy={true} /> ) : ( <Counter isFancy={false} /> )} <label> <input type="checkbox" checked={isFancy} onChange={e => { setIsFancy(e.target.checked) }} /> Use fancy styling </label> </div> ); } function Counter({ isFancy }) { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } if (isFancy) { className += ' fancy'; } return ( <div className={className} onPointerEnter={() => setHover(true)} onPointerLeave={() => setHover(false)} > <h1>{score}</h1> <button onClick={() => setScore(score + 1)}> Add one </button> </div> ); }
যখন আপনি চেকবক্স টিক বা সাফ করার সময়, কাউন্টারের স্টেট রিসেট হয় না। isFancy
যদি is true
সত্য বা false
মিথ্যা, আপনি সবসময় একটি <Counter />
একটি প্রথম চাইল্ড্ হিসাবে পেতে পারেন যা রুট App
কম্পোনেন্ট থেকে ফিরে আসা div
থেকে প্রাপ্ত হয়েছে:
এটি একই কম্পোনেন্ট একই অবস্থানে তখন, সুতরাং React এর দৃষ্টিতে এটি একই কাউন্টার:
একই অবস্থানে বিভিন্ন কম্পোনেন্টগুলি স্টেট রিসেট করে
এই উদাহরণে, চেকবক্স চিহ্নিত করলে <Counter>
এর স্থানে <p>
যুক্ত হবে:
import { useState } from 'react'; export default function App() { const [isPaused, setIsPaused] = useState(false); return ( <div> {isPaused ? ( <p>See you later!</p> ) : ( <Counter /> )} <label> <input type="checkbox" checked={isPaused} onChange={e => { setIsPaused(e.target.checked) }} /> Take a break </label> </div> ); } function Counter() { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return ( <div className={className} onPointerEnter={() => setHover(true)} onPointerLeave={() => setHover(false)} > <h1>{score}</h1> <button onClick={() => setScore(score + 1)}> Add one </button> </div> ); }
এখানে, আপনি একই অবস্থানে বিভিন্ন ধরণের কম্পোনেন্ট পরিবর্তন করেন। প্রাথমিকভাবে, <div>
এর প্রথম চাইল্ড্-টির মধ্যে একটি Counter
ছিল। But when you swapped in a p
, React removed the Counter
from the UI tree and destroyed its state. কিন্তু p কে স্থানান্তরিত করার সময়, React কাউন্টারটিকে UI-ট্রি থেকে সরে নিয়েছে এবং এর স্টেট ধ্বংস করেছে।
যখন একই অবস্থানে একটি নতুন কম্পোনেন্ট রেন্ডার করা হয়, তখন এর সম্পূর্ণ উপপাদ্যের স্টেট রিসেট হয়। এটি কার্যকর হওয়ার পদ্ধতি দেখতে একটি উদাহরণে, কাউন্টার সংখ্যা বাড়ান এবং তারপরে চেকবক্স চিহ্নিত করুন:
import { useState } from 'react'; export default function App() { const [isFancy, setIsFancy] = useState(false); return ( <div> {isFancy ? ( <div> <Counter isFancy={true} /> </div> ) : ( <section> <Counter isFancy={false} /> </section> )} <label> <input type="checkbox" checked={isFancy} onChange={e => { setIsFancy(e.target.checked) }} /> Use fancy styling </label> </div> ); } function Counter({ isFancy }) { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } if (isFancy) { className += ' fancy'; } return ( <div className={className} onPointerEnter={() => setHover(true)} onPointerLeave={() => setHover(false)} > <h1>{score}</h1> <button onClick={() => setScore(score + 1)}> Add one </button> </div> ); }
চেকবক্স ক্লিক করার সময় কাউন্টারের স্টেট রিসেট হয়। যদিও আপনি একটি Counter
রেন্ডার করেন, কিন্তু div
এর প্রথম চাইল্ড্ পরিবর্তন হয়, যা একটি section
হয়ে যায়। যখন DOM থেকে চাইল্ড্ div
সরানো হয়, তখন তার নীচের সমস্ত ট্রি (যথায়থ Counter
এবং এর স্টেটসহ) ধ্বংস হয়ে যায়।
একটি নির্দিষ্ট নিয়ম হিসাবে ধরা যায় যদি আপনি পুনরায় রেন্ডার মাধ্যমে স্টেটকে সংরক্ষণ রাখতে চান তবে আপনার ট্রির গঠনটি পুনরায় রেন্ডার থেকে একে অন্যকে “মিলানো” প্রয়োজন। যদি গঠন ভিন্ন হয়, তবে স্টেটটি ধ্বংস করা হয় কারণ React একটি কম্পোনেন্টকে ট্রি থেকে সরানোর সময় স্টেট ধ্বংস করে।
একই অবস্থানে স্টেট রিসেট করা
ডিফল্ট অবস্থানে থাকলে রিয়েক্ট কম্পোনেন্টের স্টেট সংরক্ষণ রাখে। সাধারণতঃ, এটি আপনি চান তাই এটি ডিফল্ট আচরণ হিসাবে সুসংগত। কিন্তু কখনও কখনও, আপনি কম্পোনেন্টের স্টেট রিসেট করতে চান হতে পারেন। একটি অ্যাপটি চিন্তা করুন যা দুটি খেলোয়াড় অপরপরের স্কোরগুলি পর্যালোচনা করতে দেয় প্রতিটি টার্নে:
import { useState } from 'react'; export default function Scoreboard() { const [isPlayerA, setIsPlayerA] = useState(true); return ( <div> {isPlayerA ? ( <Counter person="Taylor" /> ) : ( <Counter person="Sarah" /> )} <button onClick={() => { setIsPlayerA(!isPlayerA); }}> Next player! </button> </div> ); } function Counter({ person }) { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return ( <div className={className} onPointerEnter={() => setHover(true)} onPointerLeave={() => setHover(false)} > <h1>{person}'s score: {score}</h1> <button onClick={() => setScore(score + 1)}> Add one </button> </div> ); }
বর্তমানে, যখন আপনি খেলোয়াড় পরিবর্তন করেন, স্কোর সংরক্ষণ করা হয়। দুটি Counter
একই অবস্থানে দেখা যায়, তাই রিয়েক্ট তাদের একই Counter
হিসাবে ধরে নেয় যার প্রপার্টি person
পরিবর্তন হয়েছে।
তবে ধারণাগতভাবে, এই অ্যাপে তাদের দুটি পৃথক কাউন্টার হওয়া উচিত। তারা ইউআইতে একই জায়গায় দেখা যাতে পারে, কিন্তু একটি টেলরের জন্য একটি কাউন্টার এবং অন্যটি সারাহের জন্য একটি কাউন্টার।
এখানে দুটি উপায় আছে যখন তাদের মধ্যে স্থানান্তর করা হয়, স্টেট রিসেট করতে:
- বিভিন্ন অবস্থানে কম্পোনেন্টগুলি রেন্ডার করুন
- প্রতিটি কম্পোনেন্টের জন্য একটি স্পষ্ট পরিচয় দিন সাধারিত করুন
key
দ্বারা
অপশন ১: কম্পোনেন্টটি বিভিন্ন অবস্থানে রেন্ডার করা
যদি আপনি এই দুটি Counter
কে স্বাধীন রাখতে চান, তবে আপনি তাদের দুটি ভিন্ন অবস্থানে রেন্ডার করতে পারেন:
import { useState } from 'react'; export default function Scoreboard() { const [isPlayerA, setIsPlayerA] = useState(true); return ( <div> {isPlayerA && <Counter person="Taylor" /> } {!isPlayerA && <Counter person="Sarah" /> } <button onClick={() => { setIsPlayerA(!isPlayerA); }}> Next player! </button> </div> ); } function Counter({ person }) { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return ( <div className={className} onPointerEnter={() => setHover(true)} onPointerLeave={() => setHover(false)} > <h1>{person}'s score: {score}</h1> <button onClick={() => setScore(score + 1)}> Add one </button> </div> ); }
- প্রাথমিকভাবে,
isPlayerA
এtrue
হয়। সুতরাং, প্রথম অবস্থানেCounter
স্টেট থাকে এবং দ্বিতীয়টি খালি থাকে। - আপনি “Next Player” বাটনটি চাপানোর পরে প্রথম অবস্থানটি খালি হয়ে যায় কিন্তু দ্বিতীয় অবস্থানটি এখন একটি Counter ধারণ করে।
প্রতিটি Counter
এর স্টেট প্রতিবার DOM থেকে অপসারিত করা হলে ধ্বংস হয়ে যায়। এটি হচ্ছে কেন তারা প্রতিবার আপনি বাটনটি চাপার সময় রিসেট হয়।
এই সমাধানটি সুবিধাজনক যখন আপনার একই স্থানে কয়েকটি স্বতন্ত্র কম্পোনেন্ট রেন্ডার করা হয়। এই উদাহরণে আপনার কেবল দুটি কম্পোনেন্ট আছে, তাই JSX এ উভয়কে পৃথক ভাবে রেন্ডার করা একটি ঝামেলা নয়।
Option 2: Resetting state with a key
আরও একটি, ভাবে কম্পোনেন্টের স্টেট রিসেট করার একটি সাধারণ উপায় আছে।
আপনি সম্ভবত দেখেছেন key
গুলি যখন তালিকা রেন্ডার করা হয়। কীগুলি শুধুমাত্র তালিকার জন্য নয়! আপনি কীগুলি ব্যবহার করে রিয়েক্টকে যেকোনো কম্পোনেন্টগুলি পৃথক করতে ব্যবহার করতে পারেন। ডিফল্টভাবে, রিয়েক্ট অর্ডার ব্যবহার করে প্যারেন্টের মধ্যে (“প্রথম কাউন্টার”, “দ্বিতীয় কাউন্টার”) কম্পোনেন্টগুলি পৃথক করতে। কিন্তু কীগুলি আপনাকে বলতে দিয়ে দিতে পারে যে এটি শুধুমাত্র একটি প্রথম কাউন্টার নয়, অথবা দ্বিতীয় কাউন্টার নয়, বরং একটি নির্দিষ্ট কাউন্টার - উদাহরণস্বরূপ, টেইলরের কাউন্টার। এই ভাবে, রিয়েক্ট জানতে পারবে যে যেখানে এটি প্রদর্শিত হয়, সেখানে টেইলরের কাউন্টার!
এই উদাহরণে, প্রদর্শিত হলেও দুটি <Counter />
সমস্ত অবস্থানে স্টেট ভাগ করে না:
import { useState } from 'react'; export default function Scoreboard() { const [isPlayerA, setIsPlayerA] = useState(true); return ( <div> {isPlayerA ? ( <Counter key="Taylor" person="Taylor" /> ) : ( <Counter key="Sarah" person="Sarah" /> )} <button onClick={() => { setIsPlayerA(!isPlayerA); }}> Next player! </button> </div> ); } function Counter({ person }) { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return ( <div className={className} onPointerEnter={() => setHover(true)} onPointerLeave={() => setHover(false)} > <h1>{person}'s score: {score}</h1> <button onClick={() => setScore(score + 1)}> Add one </button> </div> ); }
টেইলর এবং সারাহের মধ্যে স্টেট সংরক্ষিত থাকলেও স্থিতিশীল রাখা হয়না। এটি কারণ আপনি তাদের জন্য ভিন্ন key
দিয়েছেন:
{isPlayerA ? (
<Counter key="Taylor" person="Taylor" />
) : (
<Counter key="Sarah" person="Sarah" />
)}
একটি key
নির্দিষ্ট করা রিয়েক্টকে বলে দেয় যে এটি স্থানের একটি অংশ হিসাবে ব্যবহার করবে, প্যারেন্টের অর্ডারের পরিবর্তে। এটি কারণেই, যখনই আপনি তাদের একই স্থানে JSX এ রেন্ডার করেন, রিয়েক্ট তাদের দুটি পৃথক কাউন্টার হিসাবে ধরে নেয়। সুতরাং, তারা কখনই স্টেট ভাগ করবে না। যেখানেই একটি কাউন্টার পর্যালোচনা করা হয়, তার স্টেট তৈরি হয়। যেকোনো সময় যখন এটি অপসারিত হয়, তখন এর স্টেট ধ্বংস হয়ে যায়। তাদের মধ্যে টগলিং করা হলে বারবার তাদের স্টেট রিসেট হয়।
একটি কী ব্যবহার করে ফর্ম রিসেট করা
ফর্ম সম্পর্কে কথা বলার সময় কী ব্যবহার করে স্টেট রিসেট করা অত্যন্ত উপযুক্ত।
এই চ্যাট অ্যাপে, <Chat>
কম্পোনেন্টটিতে টেক্সট ইনপুট স্টেট রয়েছে।:
import { useState } from 'react'; import Chat from './Chat.js'; import ContactList from './ContactList.js'; export default function Messenger() { const [to, setTo] = useState(contacts[0]); return ( <div> <ContactList contacts={contacts} selectedContact={to} onSelect={contact => setTo(contact)} /> <Chat contact={to} /> </div> ) } const contacts = [ { id: 0, name: 'Taylor', email: 'taylor@mail.com' }, { id: 1, name: 'Alice', email: 'alice@mail.com' }, { id: 2, name: 'Bob', email: 'bob@mail.com' } ];
ইনপুটে কিছু লিখে প্রেস করুন এবং তারপর “আলিস” বা “বব” এ প্রেস করুন যাতে আপনি একটি পৃথক প্রাপক প্রেস করতে পারেন। আপনি দেখবেন যে ইনপুট স্টেট সংরক্ষিত থাকে কারণ <Chat>
একই অবস্থানে ট্রি রেন্ডার করা হয়।
অনেক অ্যাপস এই অবস্থানটি কার্যকর হতে পারে, কিন্তু চ্যাট অ্যাপে এটি কার্যকর হবে না! একটি অকার্যকর ক্লিকের ফলে ব্যবহারকারীকে একটি ভুল ব্যক্তির কাছে যেটি তারা ইতিমধ্যে লিখেছেন পাঠিয়ে দেওয়া উচিত নয়। এটি ঠিক করতে, একটি key
যুক্ত করুন:
<Chat key={to.id} contact={to} />
এটি নিশ্চিত করে যে যখন আপনি একটি পৃথক প্রাপক নির্বাচন করেন, তখন Chat
কম্পোনেন্টটি পুনরায় সৃষ্টি হবে, নিচের ট্রির কোনো স্থিতির সাথে স্ক্র্যাচ থেকে নতুন স্থানে। রিয়েক্ট এটিও পুনরায় DOM উপাদানগুলি সৃষ্টি করবে, ব্যবহার করার পরিবর্তে।
এখন প্রাপক পরিবর্তন করলে সর্বদা টেক্সট ফিল্ড পরিষ্কার হয়:
import { useState } from 'react'; import Chat from './Chat.js'; import ContactList from './ContactList.js'; export default function Messenger() { const [to, setTo] = useState(contacts[0]); return ( <div> <ContactList contacts={contacts} selectedContact={to} onSelect={contact => setTo(contact)} /> <Chat key={to.id} contact={to} /> </div> ) } const contacts = [ { id: 0, name: 'Taylor', email: 'taylor@mail.com' }, { id: 1, name: 'Alice', email: 'alice@mail.com' }, { id: 2, name: 'Bob', email: 'bob@mail.com' } ];
গভীরভাবে জানুন
একটি প্রাসঙ্গিক চ্যাট অ্যাপে, আপনি সম্ভবত পূর্ববর্তী প্রাপকটি আবার নির্বাচন করলে ইনপুট স্টেট পুনরুদ্ধার করতে চান। একটি দৃষ্টিগোচর নয় কোম্পোনেন্টের জন্য স্টেটকে “সজীব” রাখতে কিছু উপায় আছে:
- আপনি বর্তমান প্রাপ্যের পরিবর্তে সমস্ত চ্যাট সংখ্যায় রেন্ডার করতে পারেন, তবে সকল অন্যান্য চ্যাটগুলি CSS দ্বারা লুকিয়ে রাখবেন। চ্যাটগুলি ট্রি থেকে অপসারিত হবেনা, তাই তাদের স্থানীয় স্টেটটি সংরক্ষিত থাকবে। এই সমাধানটি সাধারণ UI এর জন্য বিশেষভাবে ভালো কাজ করে। কিন্তু যদি লুকিয়ে রাখা ট্রি বড় হয় এবং বহুগুণ DOM নোড ধারণ করে, তবে এটি খুব নিউনতম হতে পারে।
- আপনি স্টেটটি উপরে উঠিয়ে তুলতে পারেন এবং প্রতিটি প্রাপকের জন্য মাতৃক কম্পোনেন্টে মুল্য রাখতে পারেন। এই ভাবে, যখন শিশু কম্পোনেন্টগুলি অপসারিত হয়, তখন এটি ব্যাপার নেই, কারণ প্যারেন্টটি গুরুত্বপূর্ণ তথ্য রাখে। এটি সবচেয়ে সাধারণ সমাধান।
- আপনি আপাতত রিয়েক্ট স্টেটের পাশাপাশি অতিরিক্ত একটি উৎস ব্যবহার করতে পারেন। উদাহরণস্বরূপ, ব্যবহারকারী যদি দুর্ঘটনাপূর্বক পৃষ্ঠাটি বন্ধ করে তবে মেসেজ ড্রাফটটি ধ্বংস না হয়। এটি সম্পাদন করতে, আপনি
Chat
কম্পোনেন্টটির স্টেটকেlocalStorage
থেকে পড়ে তুলতে পারেন এবং ড্রাফটগুলি সেখানেই সংরক্ষণ করতে পারেন।
আপনি যে কোনটি ব্যবহার করুন, অলিসের সাথে একটি চ্যাট অ্যাপ সংজ্ঞায়িতভাবে বিচক্ষণ একটি চ্যাট থেকে ভিন্ন, তাই বর্তমান প্রাপকের উপর ভিত্তি করে <Chat>
ট্রির জন্য একটি key
দেওয়া যায়।
পুনরালোচনা
- React স্টেটটি সম্পর্কিত কম্পোনেন্ট সমান অবস্থানে রেন্ডার হওয়ার সময় পর্যন্ত স্টেটটিকে সংরক্ষণ করে রাখে।
- স্টেট JSX ট্যাগগুলিতে রাখা হয় না। এটি আপনি যে স্থানে JSX ট্যাগটি রাখেন সেই ট্রির অবস্থার সাথে সংযুক্ত হয়।
- আপনি একটি সাবট্রি কে পুনরায় স্থাপন করতে পারেন তার স্থিতিকে পুনরায় স্থাপন করার জন্য একটি নতুন কী দিয়ে।
- কম্পোনেন্ট ডেফিনিশন নেস্ট করবেন না, অন্যথায় আপনি কাছাকাছি স্টেট পুনরায় স্থাপন করতে পারেন।
Challenge 1 of 5: অপসারিত ইনপুট পাঠ্য ঠিক করুন
এই উদাহরণে আপনি বাটন চাপার সময় একটি বার্তা প্রদর্শন করেন। তবে, বাটন চাপার সাথে সাথেই ইনপুট ভুলভাবে পুনরায় স্থাপন হয়ে যায়। কেন এটা ঘটছে? এটিকে ঠিক করুন যাতে বাটন চাপার মাধ্যমে ইনপুট পাঠ্যটি পুনরায় স্থাপন না হয়।
import { useState } from 'react'; export default function App() { const [showHint, setShowHint] = useState(false); if (showHint) { return ( <div> <p><i>Hint: Your favorite city?</i></p> <Form /> <button onClick={() => { setShowHint(false); }}>Hide hint</button> </div> ); } return ( <div> <Form /> <button onClick={() => { setShowHint(true); }}>Show hint</button> </div> ); } function Form() { const [text, setText] = useState(''); return ( <textarea value={text} onChange={e => setText(e.target.value)} /> ); }