React Class vs. Functional 元件比較 生命週期、效能與 Hooks 開發體驗

me
林彥成
2020-02-02 | 4 min.
文章目錄
  1. 1. React 開發該選 Class 還是 Functional Component?
  2. 2. React 元件教學:Class Component vs Functional Component
    1. 2.1. 核心優缺點比較:為何 Functional Component 成為主流?
    2. 2.2. 寫法比較
  3. 3. Class Component (類別元件) 與 PureComponent
    1. 3.1. PureComponent 與 React 效能優化建議
  4. 4. Functional Component 與 React Hooks 教學
    1. 4.1. 元件狀態
  5. 5. React 生命週期比較:傳統 Lifecycle vs. useEffect
  6. 6. FAQ:React 元件開發常見問題
    1. 6.1. Q1:Hooks 出現後,我還需要學習 Class Component 嗎?
    2. 6.2. Q2:Functional Component 的效能真的比 Class Component 好嗎?
    3. 6.3. Q3:為什麼 Functional Component 的狀態更新不會自動合併物件?

React 開發該選 Class 還是 Functional Component?

React 元件教學 中,Class ComponentFunctional Component 代表了兩種不同的程式典範。其核心定義在於:Class 元件基於 ES6 類別,具備完整的 React 生命週期比較 方法(如 componentDidMount);而 Functional 元件則是純函數,透過 React Hooks 教學 中的 useStateuseEffect 實作狀態與副作用。目前的 React 效能優化建議 傾向使用 Functional 寫法,因為它具備更簡潔的程式碼、更快的編譯速度且易於測試。然而,Class 元件在處理極其複雜的物件狀態合併或特定舊有生命週期時仍具優勢。掌握兩者的轉換與選型,是從初學者邁向資深 React 開發者的必經之路。


React 元件教學:Class Component vs Functional Component

在 React 開發中,建立元件主要有 Class Component (類別元件) 與 Functional Component (函式元件) 兩種方式。這篇文章將針對這兩種寫法進行深度對比,從語法差異、元件特性到 React 生命週期比較 進行詳細解析。隨著 React Hooks 教學 的普及,我們將探討為什麼現代開發更傾向於使用 Functional 寫法。

核心優缺點比較:為何 Functional Component 成為主流?

自從 React 16.8 推出 Hooks 後,Functional Component 的能力大幅提升,幾乎能完全取代 Class Component。以下是針對React 效能優化建議與開發體驗的對比表:

項目FunctionalClass-based
編譯快勝 (少了繼承 class 轉成 ES5)
更少程式碼勝 (沒有繼承)
測試容易勝 (元件週期單純)
this 的影響勝 (閉包會抓住值)this.props (state) 會改變
複雜狀態操作勝 (有 batch,可同時設多個狀態,自動合併狀態物件)
複雜的情境架構上就要切割乾淨勝 (較多元件週期可以操作)

寫法比較

接著示範同樣的功能但兩種不同的寫法,可以發現:

  • Class-based 多了 extendsrender() 的寫法,白話就是編譯過後的程式碼會比較多行
  • Functional 則是使用接近原生的寫法,不需要寫 render() 編譯後會自動在 return JSX 時叫用 react 提供的函式轉成元件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Class-based
class Input extends React.Component {
constructor() {
super();
this.state = { input: "" };

this.handleInput = this.handleInput.bind(this);
}

handleInput(e) {
this.setState({ input: e.target.value });
}

render() {
<input onChange={handleInput} value={this.state.input} />;
}
}

// Functional
function Input() {
const [input, setInput] = React.useState("");

return <input onChange={(e) => setInput(e.target.value)} value={input} />;
}

Class Component (類別元件) 與 PureComponent

Class Component 適合實作邏輯極其複雜且需要精確控制 React 生命週期 的元件。例如,當您需要手動優化效能時,常會用到 shouldComponentUpdate()

React 在狀態改變的時候會把 setState 的動作 batch 起來,所以建議使用 callback 的 function 去設定。

1
2
3
this.setState((state, props) => ({
counter: state.counter + props.increment,
}));

另外因為 Class 元件狀態主要為 Object,所以可以輕易做到動態 Key 的設定:

1
2
3
handleInputChange(event) {
this.setState({ [event.target.id]: event.target.value })
}

PureComponent 與 React 效能優化建議

PureComponent 是 React 提供的一個基礎類別,它自動實作了 shouldComponentUpdate(),透過淺比較 (Shallow Comparison) 來決定是否重新渲染,是常見的 React 效能優化建議 手法。

PS: 因為只做 shallowly compares,所以狀態盡量不要使用複雜物件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function shallowEqual(object1, object2) {
const keys1 = Object.keys(object1);
const keys2 = Object.keys(object2);
if (keys1.length !== keys2.length) {
return false;
}
for (let key of keys1) {
if (object1[key] !== object2[key]) {
return false;
}
}
return true;
}

const hero1 = {
name: 'Batman',
address: {
city: 'Gotham'
}
};
const hero2 = {
name: 'Batman',
address: {
city: 'Gotham'
}
};
// 兩層
shallowEqual(hero1, hero2); // => false

Functional Component 與 React Hooks 教學

在 React Hooks 加入前,Functional Component 主要用於純渲染 (Presentational Component)。但在 React Hooks 教學 普及後,函式元件已成為建構複雜應用的首選。

  • useState:管理元件狀態。
  • useReducer:處理複雜物件型態的狀態。
  • useEffect:處理副作用,取代傳統的 React 生命週期 方法。
  • useCallback & useMemo:用於效能優化,避免不必要的重新渲染。
  • React.memo:Functional 元件的效能優化工具,效果等同於 Class 中的 PureComponent。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
useEffect(() => {
setState((prevState) => {
// 用 Object.assign
return { ...prevState, ...props.updatedValues };
});
}, [props.updatedValues]);

// class-based 中用 callback 設定的
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});

const memoizedCallback = useCallback(fn, [...deps]);
useMemo(() => fn, [...deps]);

// 可以用來記錄前一次的值
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}

元件狀態

React 元件有 class 和 function 兩種形式,設定上分兩種:

  • class: 物件型態,透過 this.setState() 直接設定元件中的狀態
  • function: 值或是物件型態,透過 const [state, setState] = useState(); 回傳的 setState()

由於 class 的狀態一定是物件的型態,對於 Object 型態的狀態會有比較好的處理,舉例來說像物件的合併機制。

1
2
// class setState 實際上做的事情
this.setState({ ...state, value: 0 });

相對於 hook 在設定上實際上沒有針對物件做預設的物件合併機制。

1
2
//  setHookState 其實就是單純設定
const [state, setHookState] = useState(0);

React 生命週期比較:傳統 Lifecycle vs. useEffect

雖然 Functional Component 沒有傳統意義上的「生命週期方法」,但透過 useEffect Hook,我們可以達到近似甚至更靈活的效果。以下是將傳統週期改寫為 Hooks 的實戰範例:

  1. componentDidMount()
1
2
3
4
5
6
componentDidMount() {
fetchData();
}

useEffect( () => { fetchData(); }, [] );

  1. componentDidUpdate()

值得注意的是 useEffect 的條件中盡量不要使用物件,因為每次都會被看成是不同的。

1
2
3
4
5
6
7
8
9
componentDidUpdate(prevProps) {
// 常見用法(別忘了比較 prop):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}

useEffect( () => { fetchData(userID); }, [userID] );

  1. componentWillUnmount()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function clear () {
// componentWillUnmount
}

componentWillUnmount() {
clear();
}

useEffect(() => {

return () => {
clear();
};
}, []);

將元件週期改寫的實際案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
async componentDidMount() {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users`)
this.setState({ users: response.data })
};

async componentDidUpdate(prevProps) {
if (prevProps.resource !== this.props.resource) {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users`)
this.setState({ users: response.data })
}
};

const fetchUsers = async () => {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users`);

setUsers(response.data);
};

useEffect( () => { fetchUsers(users) }, [ users ] );


FAQ:React 元件開發常見問題

Q1:Hooks 出現後,我還需要學習 Class Component 嗎?

A:是的。雖然新專案大多使用 Functional Component,但業界仍有大量維護中的舊專案使用 Class 寫法。此外,某些特定功能(如:Error Boundaries 錯誤邊界)目前仍僅能透過 Class Component 實作。理解兩者的 React 生命週期比較 邏輯,能協助您更順利地進行程式碼遷移與維護。

Q2:Functional Component 的效能真的比 Class Component 好嗎?

A:在大多數場景下,效能差異微乎其微。Functional 元件的優勢主要在於「編譯後的程式碼體積較小」以及「避免了 class 實例化帶來的開銷」。真正的效能瓶頸通常來自不必要的重新渲染,這在兩者中都需要透過 React.memoshouldComponentUpdate 進行 React 效能優化建議 的實踐。

Q3:為什麼 Functional Component 的狀態更新不會自動合併物件?

A:這是設計上的刻意權衡。Class 元件的 this.setState 會自動執行 Object Merge,而 Functional 的 useState 則採取代換邏輯。這鼓勵開發者將狀態拆分為更小的原子單位(Atomic State),而非維護一個巨大的狀態物件。如果需要合併,請使用擴展運算子 setValues(prev => ({ ...prev, newField: 1 }))

Reactjs 十年回顧與技術演進