React Component Lifecycle
React Component Lifecycle
React Component Lifecycle ဆိုတာ ဘာလဲ?
React မှာ Component တစ်ခုဟာ render ဖြစ်လာတဲ့အချိန်ကနေ removed ဖြစ်သွားတဲ့အချိန်အထိ အဆင့်အမျိုးမျိုးကို ဖြတ်သန်းရပါတယ်။ ဒီအဆင့်တွေကို Component Lifecycle လို့ ခေါ်ပါတယ်။
React Component တစ်ခုရဲ့ အဓိက Lifecycle Phase တွေကတော့
- Mounting - Component ကို ပထမဆုံး render လုပ်တဲ့အချိန်
- Updating - Props ဒါမှမဟုတ် State ပြောင်းလဲလို့ Component re-render ဖြစ်တဲ့အချိန်
- Unmounting - Component removed ဖြစ်သွားတဲ့အချိန်
- Error Handling - Component Tree ထဲမှာ ဖြစ်လာတဲ့ Error တွေကို Handle လုပ်တဲ့အချိန်
တို့ ဖြစ်ပါတယ်။
Mount
│
▼
Render
│
├──── Props / State Change
│ │
│ ▼
│ Re-render
│ │
│ └───────┐
│ │
▼ │
Unmount ◄───────────────┘
React မှာ Lifecycle ကို Class Components နဲ့ Functional Components နှစ်မျိုးလုံးမှာ Handle လုပ်နိုင်ပါတယ်။ Modern React မှာတော့ Functional Components နဲ့ Hooks ကို အဓိက အသုံးပြုပါတယ်။
Mounting Phase
Component တစ်ခုကို ပထမဆုံး render လုပ်တာကို Mounting လို့ ခေါ်ပါတယ်။
function Profile() {
return <h1>Profile</h1>;
}
ဒီ Component ကို
<Profile />
လို့ render လုပ်လိုက်တဲ့အချိန်မှာ Profile Component က Mount ဖြစ်ပါတယ်။
Mount ဖြစ်ပြီးတဲ့နောက် React က Component ရဲ့ output ကို DOM မှာ commit လုပ်ပေးပါတယ်။
Updating Phase
Component Mount ဖြစ်ပြီးနောက် Props ဒါမှမဟုတ် State ပြောင်းလဲတဲ့အခါ Component က re-render ဖြစ်နိုင်ပါတယ်။ ဒါကို Updating Phase လို့ ခေါ်ပါတယ်။
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}
Button ကို နှိပ်လိုက်တဲ့အခါ
count = 0
│
setCount(1)
│
▼
count = 1
│
▼
Re-render
ဖြစ်ပါတယ်။
State ပြောင်းတိုင်း DOM တစ်ခုလုံးကို ပြန်တည်ဆောက်တာ မဟုတ်ပါဘူး။ React က render result အသစ်ကို အရင် result နဲ့ နှိုင်းယှဉ်ပြီး လိုအပ်တဲ့ DOM changes တွေကိုပဲ commit လုပ်ပေးပါတယ်။
Unmounting Phase
Render လုပ်ထားတဲ့ Component တစ်ခု removed ဖြစ်သွားတာကို Unmounting လို့ ခေါ်ပါတယ်။
{
showProfile && <Profile />;
}
showProfile က true ဖြစ်နေချိန်မှာ Profile ကို render လုပ်ထားပါမယ်။
showProfile = true
<Profile /> → Mounted
နောက်ပိုင်း
showProfile = false
<Profile /> → Unmounted
ဖြစ်သွားပါတယ်။
Component Unmount ဖြစ်တဲ့အချိန်မှာ Event Listener၊ Timer၊ Subscription၊ WebSocket Connection စတာတွေရှိရင် Cleanup လုပ်ပေးဖို့ လိုပါတယ်။
Class Component Lifecycle
React Hooks မပေါ်ခင်မှာ Component Lifecycle ကို Class Components ရဲ့ Lifecycle Methods တွေနဲ့ Handle လုပ်ခဲ့ကြပါတယ်။
Class Component တစ်ခုရဲ့ Lifecycle ကို အကြမ်းဖျင်း
Mount
│
▼
Update
│
▼
Update
│
▼
Unmount
လို့ မြင်နိုင်ပါတယ်။
Mounting - Class Component
Class Component ပထမဆုံး Mount ဖြစ်တဲ့အချိန်မှာ အဓိကအားဖြင့်
constructor()
│
▼
getDerivedStateFromProps()
│
▼
render()
│
▼
componentDidMount()
ဆိုတဲ့ Order နဲ့ Run ပါတယ်။
constructor()
constructor() က Component Instance create လုပ်တဲ့အချိန်မှာ Run ပါတယ်။
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
}
Class Components မှာ Initial State သတ်မှတ်တာ၊ Method Binding လုပ်တာတွေအတွက် အသုံးပြုခဲ့ကြပါတယ်။
getDerivedStateFromProps()
static getDerivedStateFromProps() က render() မတိုင်ခင် Run ပါတယ်။ Props ကိုအခြေခံပြီး State Update လုပ်ဖို့ လိုအပ်တဲ့ Case တွေမှာ အသုံးပြုနိုင်ပါတယ်။
static getDerivedStateFromProps(props, state) {
if (props.count !== state.count) {
return {
count: props.count,
};
}
return null;
}
Props ကို State ထဲ Copy လုပ်ထားတာက State နှစ်ခုကို Sync လုပ်နေရတဲ့ Problem ဖြစ်လာနိုင်တာကြောင့် ဒီ Method ကို ပုံမှန်အားဖြင့် မလိုအပ်ဘဲ မသုံးသင့်ပါဘူး။
render()
render() က Class Component မှာ Required Method ဖြစ်ပါတယ်။ Component က ဘာ render လုပ်မလဲဆိုတာ Return ပြန်ပေးပါတယ်။
render() {
return (
<div>
Count: {this.state.count}
</div>
);
}
Props ဒါမှမဟုတ် State ပြောင်းတဲ့အခါ render() ထပ် Run နိုင်ပါတယ်။
componentDidMount()
Component Mount ဖြစ်ပြီး DOM changes တွေ commit လုပ်ပြီးတဲ့နောက် componentDidMount() Run ပါတယ်။
componentDidMount() {
console.log("Component mounted");
}
ဒီ Method ကို အောက်ပါ Side Effects တွေအတွက် အသုံးပြုလေ့ရှိပါတယ်။
- Data Fetching
- Event Listener Setup
- Subscription Setup
- Third-party Library Setup
Updating - Class Component
Props ဒါမှမဟုတ် State ပြောင်းပြီး Component Update ဖြစ်တဲ့အခါ အဓိက Lifecycle Flow က
getDerivedStateFromProps()
│
▼
shouldComponentUpdate()
│
▼
render()
│
▼
getSnapshotBeforeUpdate()
│
▼
componentDidUpdate()
ဖြစ်ပါတယ်။
shouldComponentUpdate()
shouldComponentUpdate() က Component ကို re-render လုပ်သင့်၊ မလုပ်သင့် ဆုံးဖြတ်ဖို့ အသုံးပြုနိုင်ပါတယ်။
shouldComponentUpdate(nextProps, nextState) {
return nextState.count !== this.state.count;
}
false Return ပြန်ရင် React က အဲ့ဒီ Update အတွက် Rendering Work ကို Skip လုပ်နိုင်ပါတယ်။
ဒီ Method ကို Performance Optimization အတွက် အသုံးပြုနိုင်ပေမယ့် Application Logic ကို Control လုပ်ဖို့တော့ မသုံးသင့်ပါဘူး။
getSnapshotBeforeUpdate()
DOM Update ကို commit မလုပ်ခင် DOM က Information တစ်ခုခုကို Capture လုပ်ထားဖို့လိုတဲ့အခါ getSnapshotBeforeUpdate() ကို အသုံးပြုနိုင်ပါတယ်။
getSnapshotBeforeUpdate(prevProps, prevState) {
return null;
}
ဒီ Method က Return ပြန်ပေးတဲ့ Value ကို componentDidUpdate() မှာ snapshot အနေနဲ့ ရနိုင်ပါတယ်။ ဥပမာ Scroll Position ကို DOM Update မဖြစ်ခင် မှတ်ထားချင်တဲ့ Case မျိုးမှာ အသုံးဝင်ပါတယ်။
componentDidUpdate()
Component Update ပြီးတဲ့နောက် componentDidUpdate() Run ပါတယ်။
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
console.log("Count changed");
}
}
Previous Props/State နဲ့ Current Props/State ကို Compare လုပ်ပြီး လိုအပ်တဲ့ Side Effect တွေ Run ဖို့ အသုံးပြုနိုင်ပါတယ်။
Unmounting - Class Component
componentWillUnmount()
Component Unmount မဖြစ်ခင် componentWillUnmount() Run ပါတယ်။
componentWillUnmount() {
window.removeEventListener(
"resize",
this.handleResize
);
}
ဒီနေရာမှာ အောက်ပါ Cleanup တွေ လုပ်လေ့ရှိပါတယ်။
- Event Listener Remove လုပ်တာ
- Subscription Unsubscribe လုပ်တာ
- Timer Clear လုပ်တာ
- Connection Close လုပ်တာ
- Pending Work Cancel လုပ်တာ
Angular ရဲ့ ngOnDestroy() နဲ့ Concept အရ ဆင်တူပါတယ်။
Functional Components with Hooks
Modern React မှာတော့ Class Components ထက် Functional Components နဲ့ Hooks ကို ပိုအသုံးများပါတယ်။
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}
Functional Component မှာ
componentDidMount()
componentDidUpdate()
componentWillUnmount()
လို Lifecycle Methods တွေ မရှိပါဘူး။ External System တစ်ခုနဲ့ Synchronize လုပ်ဖို့ လိုအပ်တဲ့အခါ useEffect() ကို အသုံးပြုနိုင်ပါတယ်။
useEffect()
useEffect() က React Component ကို External System တစ်ခုနဲ့ Synchronize လုပ်ဖို့ အသုံးပြုတဲ့ Hook ဖြစ်ပါတယ်။ External System ဆိုတာ
- Browser API
- Event Listener
- Timer
- Network Connection
- WebSocket
- Third-party Library
စတာတွေ ဖြစ်နိုင်ပါတယ်။
import { useEffect } from "react";
function WindowTracker() {
useEffect(() => {
const handleResize = () => {
console.log(window.innerWidth);
};
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return <div>Window Tracker</div>;
}
ဒီ Example မှာ Component Mount ဖြစ်တဲ့အခါ Event Listener ကို Setup လုပ်ပြီး Component Unmount ဖြစ်တဲ့အခါ Listener ကို Remove လုပ်ပါတယ်။
Mount
│
▼
Effect Setup
│
▼
...
│
▼
Unmount
│
▼
Effect Cleanup
useEffect Dependency Array
useEffect() မှာ Dependency Array ကို ဘယ်လိုထည့်ထားလဲဆိုတာပေါ်မူတည်ပြီး Effect ဘယ်အချိန် Run မလဲ ကွာခြားပါတယ်။
Dependency Array မပါရင်
useEffect(() => {
console.log("Effect");
});
Effect က Component commit တိုင်း Run ပါတယ်။
Empty Dependency Array
useEffect(() => {
console.log("Mounted");
}, []);
Effect Setup က Component Mount ဖြစ်တဲ့အချိန်မှာ Run ပါတယ်။ Component Unmount ဖြစ်တဲ့အချိန်မှာ Cleanup ရှိရင် Run ပါမယ်။
Dependencies ပါရင်
useEffect(() => {
console.log("User changed:", userId);
}, [userId]);
userId ပြောင်းတဲ့အခါ Effect ကို ပြန် Run ပါတယ်။
Effect Cleanup
useEffect() ထဲက Return ပြန်ပေးတဲ့ Function ကို Cleanup Function လို့ ခေါ်ပါတယ်။
useEffect(() => {
const connection = connectToServer();
return () => {
connection.disconnect();
};
}, []);
Cleanup က Component Unmount ဖြစ်တဲ့အချိန်မှာပဲ Run တာ မဟုတ်ပါဘူး။ Dependency ပြောင်းသွားရင် Effect အသစ်ကို Setup မလုပ်ခင် Previous Effect ကို Cleanup အရင်လုပ်ပါတယ်။
userId = 1
Setup connection for user 1
│
▼
userId = 2
│
▼
Cleanup connection for user 1
│
▼
Setup connection for user 2
│
▼
Unmount
│
▼
Cleanup connection for user 2
ဒီအချက်က useEffect() ကို နားလည်တဲ့အခါ အရေးကြီးပါတယ်။ Cleanup Function ဟာ Unmount အတွက်ပဲ မဟုတ်ပါဘူး။
useEffect က Lifecycle Method မဟုတ်ပါဘူး
useEffect() ကို Class Component ရဲ့ componentDidMount()၊ componentDidUpdate() နဲ့ componentWillUnmount() အစားထိုးတယ်လို့ ရှင်းပြလေ့ရှိပါတယ်။ Beginner အနေနဲ့ Lifecycle ကို နားလည်ဖို့ ဒီလို Compare လုပ်တာ အသုံးဝင်ပါတယ်။ ဒါပေမယ့် Concept အရတော့ 1:1 Equivalent မဟုတ်ပါဘူး။
Class Component ကို
Mount
↓
Update
↓
Unmount
ဆိုတဲ့ Lifecycle အနေနဲ့ စဉ်းစားလေ့ရှိပါတယ်။ Effect ကိုတော့
Start Synchronizing
↓
Re-synchronize
↓
Stop Synchronizing
ဆိုပြီး စဉ်းစားတာ ပိုမှန်ပါတယ်။
Modern React မှာ useEffect() ရဲ့ အဓိကရည်ရွယ်ချက်က "Component Mount ဖြစ်လို့ Code တစ်ခု Run မယ်" ဆိုတာထက် Component ကို External System နဲ့ Synchronize လုပ်ဖို့ ဖြစ်ပါတယ်။
Effect မလိုတဲ့အချိန်
useEffect() ကို State ဒါမှမဟုတ် Props ပြောင်းတိုင်း Code Run ဖို့ဆိုပြီး အရာအားလုံးမှာ မသုံးသင့်ပါဘူး။
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const fullName = `${firstName} ${lastName}`;
fullName ကို firstName နဲ့ lastName ကနေ တိုက်ရိုက်တွက်လို့ရပါတယ်။ ဒါကို
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
လို Effect နဲ့ State တစ်ခု ထပ်ပြီး Sync လုပ်စရာ မလိုပါဘူး။
Render လုပ်နေချိန်မှာ တွက်လို့ရတဲ့ Value ဆိုရင် တိုက်ရိုက်တွက်တာက ပိုရိုးရှင်းပါတယ်။ useEffect() ကို External System နဲ့ Synchronize လုပ်ဖို့လိုတဲ့အချိန်မှာ အဓိက အသုံးပြုသင့်ပါတယ်။
Class Lifecycle နဲ့ Hooks Comparison
Concept အရ အနီးစပ်ဆုံး Compare လုပ်ရင်
| Class Component | Functional Component |
|---|---|
| Initial State | useState() |
componentDidMount() | Effect Setup |
componentDidUpdate() | Dependency ပြောင်းတဲ့ Effect |
componentWillUnmount() | Effect Cleanup |
shouldComponentUpdate() | memo စတဲ့ Optimization |
| Error Boundary | Error Boundary |
ဒီ Comparison က Concept ကို နားလည်လွယ်အောင် ပြထားတာဖြစ်ပြီး Class Lifecycle Methods နဲ့ Hooks တွေဟာ အတိအကျ 1:1 Equivalent မဟုတ်ပါဘူး။
Error Handling
Component Tree ထဲမှာ Rendering Error တစ်ခုဖြစ်လာတဲ့အခါ Error Boundary ကို အသုံးပြုပြီး Error ဖြစ်တဲ့ Component Tree အစား Fallback UI ပြနိုင်ပါတယ်။
Class Component နဲ့ Error Boundary တစ်ခုကို ဒီလို ရေးနိုင်ပါတယ်။
class ErrorBoundary extends React.Component {
state = {
hasError: false,
};
static getDerivedStateFromError(error) {
return {
hasError: true,
};
}
componentDidCatch(error, info) {
console.error(error, info);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
အသုံးပြုတဲ့အခါ
<ErrorBoundary>
<Profile />
</ErrorBoundary>
လို့ Wrap လုပ်နိုင်ပါတယ်။
Profile ဒါမှမဟုတ် သူ့အောက်က Component Tree မှာ Error ဖြစ်လာရင် Error Boundary က Fallback UI ကို Render လုပ်နိုင်ပါတယ်။
React Strict Mode
Development မှာ <StrictMode> သုံးထားရင် Effect Setup နဲ့ Cleanup က တစ်ကြိမ်ထက်ပို Run သလို မြင်ရနိုင်ပါတယ်။
Setup
↓
Cleanup
↓
Setup
လို Log တွေ မြင်ရနိုင်ပါတယ်။
ဒါဟာ Production မှာ Effect နှစ်ကြိမ် Run မယ်ဆိုတဲ့ အဓိပ္ပာယ် မဟုတ်ပါဘူး။ Development မှာ React က Effect ရဲ့ Setup/Cleanup Logic မှန်မမှန် စစ်နိုင်ဖို့ Extra Cycle လုပ်ပေးတာ ဖြစ်ပါတယ်။
ဒါကြောင့် Effect ရေးတဲ့အခါ
Setup → Cleanup
နှစ်ခုလုံး မှန်မှန်အလုပ်လုပ်အောင် ရေးထားဖို့ အရေးကြီးပါတယ်။
Developer တွေ React Lifecycle ကို ဘာကြောင့် နားလည်သင့်လဲ?
React Lifecycle ကို နားလည်ထားရင် Component ဘယ်အချိန် Mount ဖြစ်တယ်၊ ဘာကြောင့် re-render ဖြစ်တယ်၊ Effect ဘယ်အချိန် Run တယ်၊ Cleanup ဘယ်အချိန်လိုတယ်ဆိုတာကို ရှင်းရှင်းလင်းလင်း နားလည်နိုင်ပါတယ်။
အထူးသဖြင့် API၊ WebSocket၊ Event Listener၊ Timer၊ Subscription နဲ့ Third-party Library တွေကို React နဲ့ တွဲသုံးတဲ့အခါ Lifecycle နဲ့ Effect ကို မှန်မှန်မကိုင်တွယ်ရင် Duplicate Subscription၊ Memory Leak၊ Stale Data နဲ့ Unnecessary Effect စတဲ့ Problems တွေ ဖြစ်လာနိုင်ပါတယ်။
Modern React မှာ Lifecycle Methods တွေကို အလွတ်ကျက်ထားတာထက် Render ဘယ်လိုအလုပ်လုပ်လဲ၊ Effect ဘာအတွက်သုံးတာလဲ၊ Cleanup ဘာကြောင့်လိုတာလဲ ဆိုတဲ့ Concept ကို နားလည်ထားတာက ပိုအရေးကြီးပါတယ်။
အကျဉ်းချုပ်အားဖြင့် React Component တစ်ခုမှာ Mounting၊ Updating နဲ့ Unmounting ဆိုတဲ့ အဓိက Lifecycle Phases တွေ ရှိပါတယ်။ Class Components မှာ componentDidMount()၊ componentDidUpdate() နဲ့ componentWillUnmount() စတဲ့ Lifecycle Methods တွေနဲ့ Handle လုပ်ပြီး Modern React မှာတော့ Functional Components နဲ့ Hooks ကို အဓိက အသုံးပြုပါတယ်။ useEffect() ကို Lifecycle Method တွေရဲ့ 1:1 Replacement အဖြစ် မမြင်ဘဲ Component ကို External System နဲ့ Synchronize လုပ်ဖို့ အသုံးပြုတဲ့ Hook အဖြစ် နားလည်ထားတာက ပိုမှန်ပါတယ်။ React Lifecycle ကို သေချာနားလည်ထားခြင်းက Re-render၊ Side Effect နဲ့ Cleanup တွေကို မှန်မှန် Handle လုပ်နိုင်ပြီး ပိုကောင်းတဲ့ React Application တွေ ရေးနိုင်ဖို့ အရေးကြီးပါတယ်။