Hooks 是 React 16.8 之後出的功能,可以單純使用函式組件,而不用寫 Class component
Hooks 優點
- 更好管理有狀態的邏輯
- 把相同邏輯放在同個地方,而不是強制基於 lifecycle 方法來分拆
- Class 運作、學習較為複雜
React 目前沒有計畫移除 Class,所以是完全自由選擇使用,Hooks 也 100% 向下相容
useState 狀態管理
先看看原本的 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
| import React, { Component } from 'react';
class App extends Component { state = { count: 0 } addCount = () => { this.setState({ count: this.state.count + 1 }) } render() { const { count } = this.state; return ( <div> <h1>{ count }</h1> <button onClick={this.addCount}>add</button> </div> ); } }
export default App;
|
把 Class component 改成 Hooks ,state 跟 lifecycle 拿掉,使用 useState
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import React, { useState } from 'react';
const App = () => { const [count, setCount] = useState(0); const addCount = () => { setCount(count + 1); } return ( <div> <h1>{ count }</h1> <button onClick={addCount}>add</button> </div> ); }
export default App;
|
是不是簡短很多呢~
useState
1
| const [狀態, 設定狀態的函式] = useState(狀態初始值);
|
useState 因執行順序,必須放在函式的最上面
狀態初始值可以是任何型態,不像 class 只能用物件
useState 的 setState 需要把原本沒使用到的 state 也合併,否則會出錯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import React, { useState } from 'react';
const App = () => { const [{count1, count2}, setState] = useState({count1: 0, count2: 10}); const addCount1 = () => { setState(state => ({...state, count1: state.count1 + 1})); } const addCount2 = () => { setState(state => ({...state, count2: state.count2 + 1})); } return ( <div> <h1>{ count1 }</h1> <h1>{ count2 }</h1> <button onClick={addCount1}>add</button> <button onClick={addCount2}>add</button> </div> ); }
export default App;
|
useEffect 附加作用
每次 render 都會呼叫 useEffect,useEffect 可以做到跟 class lifecycle 一樣的效果,但 useEffect 能把相同邏輯寫在同一個函式裡,相較 class 的 lifecycle 更好管理
useEffect 會做 4 件事情
- 判斷第二個參數的陣列是否一樣,如果一樣才會繼續
- 執行上一次存下來的清理函式
- 執行
useEffect
的內容
- 把 清理函式 存下來,供下次使用
return 清理函式 等同 class 的 componentWillUnmount
1 2 3 4 5 6 7
| useEffect(() => { const onScroll = () => {}; window.addEventListener('scroll', onScroll); return () => { window.removeEventListener('scroll', onScroll); } })
|
第二參數傳入空陣列,等同 class componentDidMount
1 2 3
| useEffect(() => {
}, [])
|
等同 class componentDidUpdate
class componentDidUpdate 寫判斷條件,程式冗長
1 2 3 4 5
| componentDidUpdate(prevProps, prevState) { if (prevState.count !== this.state.count) { document.title = `You clicked ${this.state.count} times`; } }
|
useEffect 只需要傳入第二參數,如 count 與前次不一樣就會執行
1 2 3
| useEffect(() => { document.title = `You clicked ${this.state.count} times`; }, [count])
|
useContext 應用 Context
使用 Context API 時,不用使用 Consumer,useContext 可以直接用函式取得 context value
以 前一篇的 Context API 範例 來改
設定 Context
src/context/order.js
1 2 3 4
| import { createContext } from "react"; const context = createContext(); export const { Provider } = context; export default context;
|
設定 Provider,contextValue 傳入 context
改成使用 Hooks,Provider 一樣不變
src/App.js
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
| import React, { useState } from 'react'; import Header from "./Header"; import ProductList from "./ProductList"; import { Provider } from "./context/order.js";
const App = () => { const [orders, setOrders] = useState([]);
const addOrder = order => { setOrders([...orders, order]) }
const contextValue = { orders, addOrder } return ( <div> <Provider value={contextValue}> <Header /> <ProductList /> </Provider> </div> ); }
export default App;
|
使用 useContext 取得 contextValue
useContext 可以直接取得 contextValue,就不用再用 Consumer 再從函式取值
src/Header.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import React, { useContext } from 'react'; import context from "./context/order.js";
const Header = () => { const { orders } = useContext(context); return ( <header> 購物車(${orders.length}) <hr/> </header> ); }
export default Header;
|
src/ProductList.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import React from 'react'; import Product from "./Product"; const menu = [ { id: 0, name: '雞肉鍋'}, { id: 1, name: '豬肉鍋'}, { id: 2, name: '牛肉鍋'}, { id: 3, name: '海鮮鍋'}, { id: 4, name: '泡菜鍋'}, ]
const ProductList = () => { return( <ul> {menu.map(item => <Product key={item.id} {...item} />)} </ul> ) }
export default ProductList;
|
src/Product.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import React, { useContext } from 'react'; import context from "./context/order.js";
const Product = ({ id, name }) => { const { addOrder } = useContext(context); return ( <li> <label>{name}</label> <button onClick={() => {addOrder(id)}}>+</button> </li> ); }
export default Product;
|
useRef 存取組件變數
useRef 是每次指定相同的 dom
createRef 是每次創建新的 dom
範例: 自動 focus input
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { useEffect, useRef } from 'react';
const Ref = () => { const myInput = useRef(); useEffect(() => { myInput.current.focus(); }) return ( <div> <input type="text" ref={myInput}/> </div> ); }
export default Ref;
|
也可以取代 instance variable
範例: setInterval 計時器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { useState, useRef } from 'react';
const Ref = () => { const [count, setCount] = useState(0); const ref = useRef({}); const addCount = () => { ref.current = setInterval(() => { setCount(c => c + 1); }, 300); } const stopCount = () => { clearInterval(ref.current) } return ( <div> <h1>{count}</h1> <button onClick={addCount}>start</button> <button onClick={stopCount}>stop</button> </div> ); }
export default Ref;
|
🚀實體工作坊分享
最近時賦學苑開了實體體驗課,即使你對程式碼沒有概念也能上手!Lala 會帶你一起做出一個個人品牌形象網站,帶你快速了解前端的開發流程,快跟我們一起玩轉 Web 吧!
🚀線上課程分享
線上課程可以加速學習的時間,省去了不少看文件的時間XD,以下是我推薦的一些課程
想學習更多關於前後端的線上課程,可以參考看看。
Hahow 有各式各樣類型的課程,而且是無限次數觀看,對學生或上班族而言,不用擔心被時間綁住
如果你是初學者,非常推薦六角學院哦!
剛開始轉職也是上了六角的課,非常的淺顯易懂,最重要的是,隨時還有線上的助教幫你解決問題!
Udemy 裡的課程非常的多,品質普遍不錯,且價格都滿實惠的,CP值很高!
也是很多工程師推薦的線上課程網站。