useSyncExternalStore একটি কম্পোনেন্টকে React-এর বাইরে থাকা ডেটা পড়ার সুযোগ দেয়, যাতে কনকারেন্ট রেন্ডারিংয়ের (concurrent rendering) কারণে সৃষ্ট ভিজ্যুয়াল গ্লিচ (visual glitches) এড়ানো যায়। এটি useEffect ব্যবহার করে এক্সটার্নাল ভ্যালুগুলোকে লোকাল স্টেটে নিয়ে আসার পুরনো পদ্ধতিটিকে প্রতিস্থাপন করে, যা "tearing" ঘটাতে পারে – অর্থাৎ একই রেন্ডারের সময় UI-এর বিভিন্ন অংশ ভিন্ন ভিন্ন ভ্যালু দেখাতে পারে।
কেন পুরনো পদ্ধতি কনকারেন্সির ক্ষেত্রে কাজ করে না
React 18-এর আগে, সাধারণ নিয়মটি ছিল:
const [value, setValue] = useState(initial);
useEffect(() => {
const unsubscribe = externalSource.subscribe(v => setValue(v));
return unsubscribe;
}, []);
Effect-টি কম্পোনেন্ট রেন্ডার হওয়ার পরে চলে। যখন React-এর কনকারেন্ট রেন্ডারার কোনো রেন্ডারকে পজ (pause), অ্যাবোর্ট (abort) বা রিপ্লে (replay) করার সিদ্ধান্ত নেয়, তখন effect-টি UI-এর তুলনায় ভিন্ন সময়ে চলতে পারে যা স্টেটটি পড়ছে। এর ফলে একটি অমিল তৈরি হয়: একটি কম্পোনেন্ট হয়তো পুরনো ভ্যালু পড়েছে, অন্যটি নতুনটি, এবং স্ক্রিনে সাময়িকভাবে অসঙ্গতিপূর্ণ ডেটা দেখা যায়। এই "tearing" পুরোপুরি সিনক্রোনাস জগতে ক্ষতিকারক নয়, কিন্তু React যখন সমান্তরালভাবে (in parallel) UI-এর একাধিক ভার্সন রেন্ডার করে, তখন এটি একটি দৃশ্যমান বাগ হয়ে দাঁড়ায়।
কীভাবে useSyncExternalStore সমস্যাটি সমাধান করে
এই হুকটির জন্য দুটি কলব্যাক প্রয়োজন:
- subscribe – একটি লিসেনার রেজিস্টার করে এবং এটি রিমুভ করার জন্য একটি ফাংশন রিটার্ন করে।
- getSnapshot – এক্সটার্নাল সোর্সের বর্তমান ভ্যালু রিটার্ন করে।
React কম্পোনেন্ট মাউন্ট (mount) হওয়ার সময় subscribe কল করে এবং আনমাউন্ট (unmount) হওয়ার সময় এটি ডিসপোজ (dispose) করে। প্রতিটি রেন্ডারের সময় এটি getSnapshot কল করে এবং গ্যারান্টি দেয় যে একই এক্সটার্নাল স্টোর ব্যবহার করা সমস্ত কম্পোনেন্ট হুবহু একই স্ন্যাপশট পড়বে। যেহেতু রিড (read) প্রক্রিয়াটি রেন্ডার ফেজের ভেতরে ঘটে, তাই রেন্ডারার পুরো ট্রি জুড়ে আপডেটগুলো সমন্বয় করতে পারে, যা tearing দূর করে।
Zustand এবং Redux-এর মতো লাইব্রেরিগুলো ইতিমধ্যেই তাদের স্টোরগুলোকে এই হুক দিয়ে র্যাপ (wrap) করেছে, যার কারণে তারা React 18-এর কনকারেন্ট ফিচারগুলোর সাথে সরাসরি কাজ করতে পারে।
হুকটি যাতে ভুলভাবে কাজ না করে তার নিয়মাবলী
- শুধুমাত্র যা প্রয়োজন তা-ই রিটার্ন করুন – একটি বড় অবজেক্ট রিটার্ন করলে React-কে প্রতিবার আপডেটের সময় ডিপ স্ট্রাকচার (deep structures) তুলনা করতে বাধ্য করে। একটি ছোট বা ন্যারো (narrow) স্ন্যাপশট চেঞ্জ ডিটেকশন সহজ রাখে।
- রেফারেনশিয়াল আইডেন্টিটি (referential identity) বজায় রাখুন – যদি এক্সটার্নাল ভ্যালু পরিবর্তিত না হয়, তবে getSnapshot অবশ্যই একই অবজেক্ট রেফারেন্স রিটার্ন করতে হবে। প্রতিবার কল করার সময় একটি নতুন অবজেক্ট রিটার্ন করলে ইনফিনিট রেন্ডার লুপ (infinite render loop) তৈরি হবে, কারণ React প্রতি রেন্ডারেই পরিবর্তন দেখতে পাবে।
- ব্যবহার শুধুমাত্র এক্সটার্নাল সোর্সের মধ্যে সীমাবদ্ধ রাখুন – লোকাল কম্পোনেন্ট স্টেট
useStateবাuseReducer-এ থাকা উচিত। শুধুমাত্র ইন্টারনাল ভ্যালুর জন্য এক্সটার্নাল-স্টোর হুক ব্যবহার করা অপ্রয়োজনীয় জটিলতা তৈরি করে।
কখন useSyncExternalStore ব্যবহার করবেন
- ব্রাউজার API র্যাপ (wrap) করতে (window size, network-online status, media queries)।
- একটি সাধারণ JavaScript স্টোর কানেক্ট করতে যা React সম্পর্কে জানে না।
- প্রপ-ড্রিলিং (prop-drilling)-এর পরিবর্তে একটি গ্লোবাল রিড-অনলি (read-only) সোর্স ব্যবহার করতে যেখানে অনেকগুলো কম্পোনেন্ট সাবস্ক্রাইব করে।
পাল্টা যুক্তি
সহজ এবং খুব কম পরিবর্তিত হয় এমন ডেটার জন্য useState + useEffect প্যাটার্ন এখনও কাজ করে, বিশেষ করে যখন কোনো অ্যাপ্লিকেশনে কনকারেন্ট ফিচারগুলো চালু করা থাকে না। এটি একটি লো-লেভেল অ্যাপ্রোচ যা কিছু ডেভেলপারের কাছে বোঝা সহজ কারণ সাবস্ক্রিপশন লজিকটি সরাসরি ইফেক্টের (effect) মধ্যে থাকে। তবে, কনকারেন্ট রেন্ডারিং চালু করা হলে এর বিনিময়ে "tearing"-এর ঝুঁকি থাকে। যে প্রজেক্টগুলোতে ইতিমধ্যে React 18-এর নতুন ক্ষমতাগুলো ব্যবহার করা হচ্ছে, সেখানে useSyncExternalStore-এর অতিরিক্ত নিরাপত্তা সামান্য বাড়তি কোড (boilerplate)-এর চেয়ে অনেক বেশি গুরুত্বপূর্ণ।
পরবর্তীতে যা খেয়াল রাখবেন
- নিশ্চিত করুন যে আপনার স্টোরের getSnapshot একটি স্টেবল (stable) রেফারেন্স রিটার্ন করে; ডেভেলপমেন্টের সময় রেফারেন্সটি
console.logকরে দেখলে ভুলবশত অবজেক্ট রিক্রিয়েশন (object recreation) ধরা পড়তে পারে। - সাবস্ক্রিপশন ফাংশনটি লাইটওয়েট (lightweight) রাখুন – ভারী কাজ এক্সটার্নাল সোর্সের ভেতরে হওয়া উচিত, React-এ পাস করা কলব্যাকের ভেতরে নয়।
- React-এর কনকারেন্ট মোডে কম্পোনেন্টগুলো টেস্ট করুন (যেমন,
ReactDOM.createRoot-এর সাথেactব্যবহার করে) যাতে নিশ্চিত হওয়া যায় যে কোনো tearing দেখা দিচ্ছে না।
একটি লাইভ ডেমো হুকটির কাজ সরাসরি দেখায়, যেখানে React-এর বাইরে থাকা একটি সাধারণ কাউন্টার সিঙ্ক করা হচ্ছে এবং কোনো ভিজ্যুয়াল গ্লিচ ছাড়াই একাধিক কম্পোনেন্ট আপডেট করা হচ্ছে। ডেমোটি https://usesyncexternalstore.vercel.app/ লিঙ্কে পাওয়া যাচ্ছে। সোর্স কোডটি GitHub-এ রয়েছে এবং একটি বিস্তারিত লেখায় এর ইমপ্লিমেন্টেশন ডিটেইলস ব্যাখ্যা করা হয়েছে।
সারকথা (Takeaway): এমন একটি বিশ্বে যেখানে React UI-এর বিভিন্ন অংশ অগোছালোভাবে (out of order) রেন্ডার করতে পারে, useSyncExternalStore হলো এক্সটার্নাল ডেটা এবং কম্পোনেন্ট রেন্ডারিংয়ের মধ্যে একটি নির্ভরযোগ্য সেতু। এটি ভিজ্যুয়াল কনসিস্টেন্সি বজায় রাখে, গ্লোবাল স্টোরের জন্য স্কেল করতে পারে এবং ইতিমধ্যেই জনপ্রিয় স্টেট-ম্যানেজমেন্ট লাইব্রেরিগুলোতে অন্তর্ভুক্ত রয়েছে। যেখানেই আপনাকে নন-React স্টেট পড়ার প্রয়োজন হয়, সেখানেই এটি গ্রহণ করুন এবং এর পূর্ণ সুবিধা পেতে স্ন্যাপশটগুলোকে ছোট এবং স্টেবল রাখুন।
