How to handle the Effect firing twice in development?
রিয়াক্ট ইন্টেনশনালি প্রতিটা কম্পোনেন্টকে দুইবার করে রান করে যাতে আমরা সহজেই বুঝতে পারি যে কোডে কোন বাগ রয়েছে কিনা, তাই ডেভেলপমেন্ট মুডে আমরা যখনি দেখবো যে আমাদের কম্পোনেন্ট দুইবার করে রান হয়েছে তখন আমরা এটা ভাববোনা যে, কিভাবে আমাদের কম্পোনেন্ট একবার রান করবো বরং এটা ভাববো যে কিভাবে আমাদের ইফেক্টগুলোকে ঠিক ভাবে করতে পারি যাতে আমাদের কম্পোনেন্ট দুইবার মাউন্ট হলেও যেন ইফেক্ট ঠিকভাবে কাজ করে?
সাধারণভাবেই এটার উত্তর হলো, ইফেক্টের ক্লিন-আপ ফাংশনের ইমপ্লিমেন্ট করা। আমরা যদি সঠিকভাবে ক্লিন-আপ ফাংশন ইমপ্লিমেন্ট করতে পারি, তাহলে আমদের কম্পোনেন্ট দুইবার করে মাউন্ট হলেও সঠিকভাবে কাজ করবে।
Don’t use refs to prevent Effects from firing
একটা সাধারণ ভুল যেটা অনেকেই করে তা হলো ref
এর মাধ্যমে ইফেক্টকে দুইবার করে রান হওয়া বন্ধ করে।
const connectionRef = useRef(null);
useEffect(() => {
// ❌ This wont fix the bug!!!
if (!connectionRef.current) {
connectionRef.current = createConnection();
connectionRef.current.connect();
}
}, []);
এটা করলে হয়তো ডেভেলপমেন্ট মোডে আপনি দেখতে পারেন যে আপনার কানেকশন একবার হয়ছে,কিন্তু এতে কিন্তু বাগ ফিক্স হয়না। যখন ইউজার নেভিগেট করে অন্য পেজে চলে যাবে এবং ফিরে আসলে আবার নতুন করে কানেকশন হবে, কিন্তু পুর্বের কানেকশন কিন্তু ক্লোজ করা হয়নি। তাই এটা মেমরি লিক করবে।
তাই এটা ফিক্স করার জন্য অবশ্যই ক্লিন-আপ ফাংশন ইমপ্লিমেন্ট করা লাগবে।
নিচে আমরা কয়েকটা কমন প্যাটার্নের উদাহরণ দেখবো যাতে আমরা বুঝতে পারি যে কখন আমাদের ক্লিন-আপ ফাংশন লেখা লাগবে আর কখন লাগবেনা।
Controlling non-React widgets
যখন আমরা কোন UI এলিমেন্ট এড করবো যা আমাদের রিয়াক্টে লেখা নয়, উদাহরণ স্বরূপ যদি আমরা একটা ইমেজ এড করি এবং তার একটা জুমলেভেল সেট করে দেই ইফেক্ট দিয়ে তাহলে আমাদের ক্লিন-আপ ফাংশনের কোন দরকার নেই, কেননা কম্পোনেন্ট যতবার মাউণ্ট হউক না কেন, একই কপোনেন্টে বার বার একই ভ্যালু পাবে। তাই এখানে ক্লিন-আপের কোন দরকার নেই।
useEffect(() => {
const map = mapRef.current;
map.setZoomLevel(zoomLevel);
}, [zoomLevel]);
আবার কিছু কিছু API আছে যেগুলা একসাথে দুইবার রান হলে ইরর দেয়, যেমন HTML এর dialog
ট্যাগ এর একটা বিল্ট-ইন মেথড আছে showModal
নামে। এখানে যদি দুইবার showModal
কল হয় তাহলে ইরর দিতে পারে, তাই এখানে ক্লিন-আপ করতে হবে।
useEffect(() => {
const dialog = dialogRef.current;
dialog.showModal();
return () => dialog.close();
}, []);
Subscribing to events
যখনি আমরা ইফেক্টের ভিতরে কোন ইভেন্ট লিসেনার এড করবো সেটা অবশ্যই আমদের ক্লিন-আপ করা লাগবে।
useEffect(() => {
function handleScroll(e) {
console.log(window.scrollX, window.scrollY);
}
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
Triggering animations
যখন আমরা ইফেক্টের ভিতরে কোন কম্পোনেন্টে এনিমেশন এড করবো তখন অবশ্যই ক্লিন-আপ এড করা লাগবে, যাতে কম্পোনেন্ট আনমাউন্ট হলে এনিমেশন ও রিসেট হয়, নাহলে এটা ইউজারের এক্সপেরিয়ন্স কে নষ্ট করতে পারে।
useEffect(() => {
const node = ref.current;
node.style.opacity = 1; // Trigger the animation
return () => {
node.style.opacity = 0; // Reset to the initial value
};
}, []);