本篇內容參考自 Overreacted 網站的 “How Are Function Components Different from Classes?”。
現在幾乎所有 React 新專案都用函式元件(Function Component),好像 class 元件逐漸被淘汰。但到底這兩者之間,真正的差別是什麼?
今天我們來聊聊 Overreacted 那篇文章提到的一個關鍵差異:函式元件的 closure 捕捉 vs class 的 this 可變性。
class 宣告,有 this.state、this.props,方法通常讀取 this 上的值。useState、useEffect 等 Hook 管理狀態與副作用。這篇 Overreacted 的觀點是:真正的差異在於 行為模型 (behavior model),特別是在 callback 或延遲執行時,函式元件的 closure 捕捉/class 的 this 更新方式的不同。
this 可變性在 Function component 裡,每一次 render,useEffect 裡的 callback function 都會被重新建立,而它會閉包捕捉當下的 state / props。
假如這個 callback 被延遲執行(如 setTimeout、事件裡),它會用當時被捕捉的值,即使後來 state / props 有變,也不會去拿最新的。
在 Class component 裡,callback 通常用 this.state 或 this.props,而 this 是 mutable(可變的),會隨著每次 render 更新。
所以 callback 被執行時,讀到的就是最新的 this.state / this.props。
這就是兩者在「非同步 / 延遲行為」下會有不同行為的原因。
Class 版本:
class Counter extends React.Component {
state = { count: 0 };
componentDidMount() {
setTimeout(() => {
console.log("class count:", this.state.count);
}, 1000);
}
render() {
return <button onClick={() => this.setState({ count: this.state.count + 1 })}>
Count: {this.state.count}
</button>;
}
}
setTimeout 執行前按了一下按鈕,class 版本會在 log 裡拿到最新的 this.state.count。Function 版本:
function CounterFn() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
setTimeout(() => {
console.log("fn count:", count);
}, 1000);
}, [count]);
return <button onClick={() => setCount(count + 1)}>
Count: {count}
</button>;
}
setTimeout 裡的 callback 捕捉到了那次 render 的 count。即便後來 count 更新,這個 callback 還是印出最初那次捕捉的數字。寫一個 unction component,有一個按鈕點擊會 count +1,同時 setTimeout 在 2 秒後印出 count。
問:印出什麼值?為什麼?怎麼改寫,才能印最新的 count?
在 React 中,Function component和 Class component 最大的差異在於它們如何取得 state 和 props。
在 Function component裡,callback 會「閉包(closure)」它定義當下那一次 render 的 state/props。
所以如果這個 callback 被延遲執行(例如在setTimeout、事件、或 Promise 之後才跑),它讀到的值可能是舊的(stale >value)。在 Class component裡,callback 通常是透過
this.state/this.props來存取。
因為this永遠指向同一個實例,this.state和this.props在每次更新後都會被 React 換成最新的,所以 >callback 幾乎總是能讀到最新的值。
Function components differ from class components mainly by how they capture state and props.
In function components, callbacks “closure” the state/props from the render in which they were defined, so delayed callbacks can see stale values.
In class components, callbacks refer tothis.state/this.props, which always reflect the latest values.