終究都要學 React 何不現在學呢? - React 基礎 - Class?Hooks? - (3)

前言

Function Component?Class Component?React Hooks?這些到底是什麼東西呢?所以這一篇我們就要來認識一下 React 中的寫法以及其差異。

Function Component 與 Class Component

現行 React 基本上有兩種寫法,分別是 Function Component 與 Class Component,或許你沒聽過 Function Component,但你一定知道 React Hooks,而 React Hooks 所指的就是 Function Component,Class Component 則是 React 早期寫法而已。

這邊稍微挪用一下前面章節的 Class Component 範例程式碼:

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
29
30
31
32
33
34
35
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};

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

sayHi() {
window.alert('Hello Ray.');
}

handleClick() {
this.setState((prevState) => ({
count: prevState.count += 1,
}));
}

render() {
return (
<div>
<button onClick={this.handleClick}>
Count is: { this.state.count }
</button>
<hr />
<button type="button" onClick={ this.sayHi }>打招呼</button>
</div>
);
}
}

const app = ReactDOM.createRoot(document.getElementById('app'));
app.render(<App />);

Class Component 非常好辨別,就是使用 ES6 的 Class 語法來建立,而基本上 Component 的概念依照官方文件來講其實就是一個 function 而裡面包含了 React Element 而已。

那麼 Function Component 呢?Function Component 又是如何呢?這邊讓我們看一下將上方 Class Component 轉成 Function Component 之後的樣子吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function App (){
const [count, setCount] = React.useState(0);

function sayHi () {
window.alert('Hello Ray.');
}
function handleClick() {
setCount((prevState) => prevState += 1)
}
return (
<div>
<button type="button" onClick={ handleClick }>
Count is: { count }
</button>
<hr />
<button type="button" onClick={ sayHi }>打招呼</button>
</div>
)
}

const app = document.querySelector('#app');
const root = ReactDOM.createRoot(app);
root.render(<App />);

透過這兩個範例程式碼我們可以看到 Function Component 比 Class Component 簡潔許多,甚至少了一些艱深的語法,例如 ES6 Classextendsconstructor 以及最可怕的 this

光一個 this 就可以出各種 this 考題考倒一推人,如果可以的話,我也會選擇不要寫 this

那麼前面也有提到 Function Component 其實就是 React Hooks,Reach Hooks 是 React 在 16.8 新增的功能,而它主打的就是你不用再寫 class 就可以直接使用 React 相關功能。

看到這邊如果你是一名 Vue 開發者,你應該會感覺起來跟 Vue 非常相似,因為 Vue 也是一樣提供了兩種寫法,分別是…

選項式 API (Options API):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const { createApp } = Vue;

const app = createApp({
data() {
return {
count: 0,
};
},
methods: {
sayHi() {
window.alert('Hello Ray.');
},
},
});

app.mount('#app');

以及組合式 API(Composition API)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const { createApp, ref } = Vue;

const app = createApp({
setup() {
const count = ref(0);

const sayHi = () => {
window.alert('Hello Ray.');
};

return {
count,
sayHi
}
},
});

app.mount('#app');

其實你會發現 Composition API 與 React Hooks 有些地方非常相似,因此你也可以把 Composition API 叫做 Vue Hooks 呢。(笑)

而 Vue 作者也有說 Composition API 主要是受 React Hooks 的啟發,因此可以在 React 與 Vue 之間發現兩者的差異。

React Hooks 使用規則

接著讓我們聊一下使用 React Hook 有哪些使用規則與限制吧。

基本上 React Hooks 只有兩個重點限制,分別是

  • 只能在函式頂層使用
  • 只能用於 Function Component

只能在函式頂層使用

什麼是「只能在函式頂層使用」呢?舉例下方這幾種寫法都是會出現錯誤的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function App (){
// ❌ Error!不符合 React Hooks 規範
function sayHi () {
const [count, setCount] = React.useState(0);
}

// ❌ Error!不符合 React Hooks 規範
if(true) {
const [count, setCount] = React.useState(0);
}

// ❌ Error!不符合 React Hooks 規範
for(let i = 0; i < 5; i += 1) {
const [count, setCount] = React.useState(0);
}

return (
<div>
<button type="button" onClick={ sayHi }>打招呼</button>
</div>
)
}

正確寫法就只能寫在函式最外層

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function App (){
// ✅ Good!符合 React Hooks 規範
const [count, setCount] = React.useState(0);

function sayHi () {
window.alert('Hello!')
}

return (
<div>
<button type="button" onClick={ sayHi }>打招呼</button>
</div>
)
}

只能用於 Function Component

React Hooks 本身是基於 Function Component 所設計的,因此就只能在 Function Component 中使用,是不能在 Class Components 中使用的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class App extends React.Component {
constructor(props) {
const [count, setCount] = React.useState(0);
super(props);
this.state = {
count: 0,
};
}

sayHi() {
window.alert('Hello Ray.');
}

render() {
return (
<div>
<button type="button" onClick={ this.sayHi }>打招呼</button>
</div>
);
}
}

其實也不用特別去記,因為如果你真的做這種事情的話,你也可以在 Console 看到以下提示訊息:

1
2
3
4
5
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

除此之外 React Hooks 與 Vue3 composition API 一樣都是向後兼容的,因此是可以一個專案內同時擁有 Class Component 跟 Function Component 寫法。

這時候你可能會延伸一個問題「那我該用 Class Component 開發還是 Function Component?還是兩者一起?」其實這個問題在官方文件就有提到,所以就直接擷取官方文件的說明

當你準備好時,我們鼓勵開始使用 Hook 撰寫你新的 component。確保你團隊的成員們使用 Hook 並熟悉本文件。我們並不鼓勵你重寫現有的 class component 成 Hook,除非你已經計劃重寫它們(例如:修正 bug)。
你不可以在 class component 內使用 Hook,但你絕對可以在單個 tree 中將 class 和 function component 與 Hook 混合使用。無論是 class 或 function component,使用 Hook 是該 component 實作的細節。從長遠來看,我們期待 Hook 可以是大家撰寫 React component 的主要方式。

因此其實看得出來 React Hooks 必定會成為主流,畢竟官方比較期望你使用 React Hooks。

後記

本文將會同步更新到我的部落格