前言
React 在 v16.8 的版本中推出了 React Hooks 新特性。在我看來,使用 React Hooks 相比于從前的類組件有以下幾點好處:
- 代碼可讀性更強,原本同一塊功能的代碼邏輯被拆分在了不同的生命周期函數中,容易使開發者不利于維護和迭代,通過 React Hooks 可以將功能代碼聚合,方便閱讀維護;
- 組件樹層級變淺,在原本的代碼中,我們經常使用 HOC/render props 等方式來復用組件的狀態,增強功能等,無疑增加了組件樹層數及渲染,而在 React Hooks 中,這些功能都可以通過強大的自定義的 Hooks 來實現;
關于這方面的文章,我們根據使用場景分別進行舉例說明,幫助你認識理解并可以熟練運用 React Hooks 大部分特性。
博客 github地址為:https://github.com/fengshi123/blog
一、State Hook
1、基礎用法
1
2
3
4
5
6
7
8
9
10
11
|
function State(){ const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ) } |
2、更新
更新分為以下兩種方式,即直接更新和函數式更新,其應用場景的區分點在于:
- 直接更新不依賴于舊 state 的值;
- 函數式更新依賴于舊 state 的值;
1
2
3
4
5
|
// 直接更新 setState(newCount); // 函數式更新 setState(prevCount => prevCount - 1); |
3、實現合并
與 class 組件中的 setState 方法不同,useState 不會自動合并更新對象,而是直接替換它。我們可以用函數式的 setState 結合展開運算符來達到合并更新對象的效果。
1
2
3
4
|
setState(prevState => { // 也可以使用 Object.assign return {...prevState, ...updatedValues}; }); |
4、惰性初始化 state
initialState 參數只會在組件的初始渲染中起作用,后續渲染時會被忽略。其應用場景在于:創建初始 state 很昂貴時,例如需要通過復雜計算獲得;那么則可以傳入一個函數,在函數中計算并返回初始的 state,此函數只在初始渲染時被調用:
1
2
3
4
|
const [state, setState] = useState(() => { const initialState = someExpensiveComputation(props); return initialState; }); |
5、一些重點
(1)不像 class 中的 this.setState ,Hook 更新 state 變量總是替換它而不是合并它;
(2)推薦使用多個 state 變量,而不是單個 state 變量,因為 state 的替換邏輯而不是合并邏輯,并且利于后續的相關 state 邏輯抽離;
(3)調用 State Hook 的更新函數并傳入當前的 state 時,React 將跳過子組件的渲染及 effect 的執行。(React 使用 Object.is 比較算法 來比較 state。)
二、Effect Hook
1、基礎用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function Effect(){ const [count, setCount] = useState(0); useEffect(() => { console.log(`You clicked ${count} times`); }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ) } |
2、清除操作
為防止內存泄漏,清除函數會在組件卸載前執行;如果組件多次渲染(通常如此),則在執行下一個 effect 之前,上一個 effect 就已被清除,即先執行上一個 effect 中 return 的函數,然后再執行本 effect 中非 return 的函數。
1
2
3
4
5
6
7
|
useEffect(() => { const subscription = props.source.subscribe(); return () => { // 清除訂閱 subscription.unsubscribe(); }; }); |
3、執行時期
與 componentDidMount 或 componentDidUpdate 不同,使用 useEffect 調度的 effect 不會阻塞瀏覽器更新屏幕,這讓你的應用看起來響應更快;(componentDidMount 或 componentDidUpdate 會阻塞瀏覽器更新屏幕)
4、性能優化
默認情況下,React 會每次等待瀏覽器完成畫面渲染之后延遲調用 effect;但是如果某些特定值在兩次重渲染之間沒有發生變化,你可以通知 React 跳過對 effect 的調用,只要傳遞數組作為 useEffect 的第二個可選參數即可:如下所示,如果 count 值兩次渲染之間沒有發生變化,那么第二次渲染后就會跳過 effect 的調用;
1
2
3
|
useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // 僅在 count 更改時更新 |
5、模擬 componentDidMount
如果想只運行一次的 effect(僅在組件掛載和卸載時執行),可以傳遞一個空數組([ ])作為第二個參數,如下所示,原理跟第 4 點性能優化講述的一樣;
1
2
3
|
useEffect(() => { ..... }, []); |
6、最佳實踐
要記住 effect 外部的函數使用了哪些 props 和 state 很難,這也是為什么 通常你會想要在 effect 內部 去聲明它所需要的函數。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
// bad,不推薦 function Example({ someProp }) { function doSomething() { console.log(someProp); } useEffect(() => { doSomething(); }, []); //
延伸 · 閱讀
精彩推薦
|