以前只能在 Class Component 處理 State,現在也能在 Function Component 以 State Hook 處理 State。
Version
macOS Mojave 10.14.6
WebStorm 2019.2.3
Node 12.11.0
Yarn 1.19.0
create-react-app 3.1.2
React 16.10.1
Class Component
App.js
import React, { Component } from 'react';
export default class extends Component {
state = {
count: 0,
};
addCount = () => {
this.setState({
count: this.state.count + 1
});
};
render() {
return (
<div>
<button onClick={ this.addCount }>+</button>
<div>{ this.state.count }</div>
</div>
);
}
}
14 行
render() {
return (
<div>
<button onClick={ this.addCount }>+</button>
<div>{ this.state.count }</div>
</div>
);
}
傳統 JSX 要寫在 render()
內,且必須使用 this
才能抓到 state 與 method。
第 4 行
state = {
count: 0,
}
宣告 state
object 儲存 count
state。
第 8 行
addCount = () => {
this.setState({
count: this.state.count + 1
});
};
定義 click
的 event handler:addCount()
method,使用 setState()
寫入新的 state。
Class component 寫法遵循 OOP 風格:使用 side effect 更改 state,且大量使用
this
與method
Function Component
App.js
import React, { useState } from 'react';
export default () => {
let [count, setCount] = useState(0);
let addCount = () => setCount(count + 1);
return (
<div>
<button onClick={ addCount }>+</button>
<div>{ count }</div>
</div>
);
};
App
由 class 變成 function。
第 1行
import React, { useState } from 'react';
將 useState()
import 進來。
第 7 行
return (
<div>
<button onClick={ addCount }>+</button>
<div>{ count }</div>
</div>
);
不再需要 render()
,只需 return JSX 即可。
也由於 state 與 function 都宣告在 App()
內,連 this
也不需要了。
第 4 行
let [count, setCount] = useState(0);
React 提供了 useState()
hook,傳進 state 的初始值,也就是 0
,回傳兩個值,一個為 state,另外一個為 state 的 setter,習慣命名為 setXXX()
。
第 6 行
let addCount = () => setCount(count + 1);
定義 click
的 event handler:addCount()
function,使用 useState()
hook 提供的 setCount()
寫入新的 state。
Function component 寫法遵循 FP 風格:不使用 side effect 更改 state,也沒有使用
this
與method
,全都是一進一出的 pure function,且程式碼更為精簡
Custom Hook
Q:若將來 component 太大要重構,或其他 component 要重複使用這段邏輯該怎麼辦 ?
counter.js
import { useState } from 'react';
export default () => {
let [count, setCount] = useState(0);
let addCount = () => setCount(count + 1);
return [
count,
addCount,
];
};
我們將 count
state 與 addCount()
抽成 useCount()
hook,單獨放在 counter
module 中。
由於 JSX 需要的只有 count
state 與 addCount()
function,最後再將這兩個以 array 方式傳回。
React 官方建議 custom hook 都以 use
開頭。
App.js
import React from 'react';
import useCount from './counter';
export default () => {
let [count, addCount] = useCount();
return (
<div>
<button onClick={ addCount }>+</button>
<div>{ count }</div>
</div>
);
};
App()
只要接收 useCount()
hook 所傳回的 state 與 function 即可。
我們可以發現由於 function component 的 hook 寫法,因為都是沒有 side effect 的 pure function,所以特別容易重構,不用再擔心
this
問題,只需簡單的 extract function,然後搬到其他 module 即可
Conclusion
- 有了 state hook,使得原本必須在 class component 才能有的 state,目前在 function component 也能實現
- 由於 function component 都是 pure function,因此特別容易重構成 custom hook
- Component 間牽涉 state 的邏輯若有重複,只要簡單抽成 custom hook 再 import 進來即可,不必再使用 render props 或 higher-order component
Reference
Reed Barger, Introducing the useState Hook
React, Hooks at a Glance
React, Using the State Hook
React, Building Your Own Hooks