Lala Code

Lala 的前端大補帖,歡迎一起鑽研前端技術😊

0%

React常用Hooks

hooks

Hooks 是 React 16.8 之後出的功能,可以單純使用函式組件,而不用寫 Class component

Hooks 優點

  1. 更好管理有狀態的邏輯
  2. 把相同邏輯放在同個地方,而不是強制基於 lifecycle 方法來分拆
  3. 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); // 或 setCount(c => c + 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 件事情

  1. 判斷第二個參數的陣列是否一樣,如果一樣才會繼續
  2. 執行上一次存下來的清理函式
  3. 執行useEffect的內容
  4. 把 清理函式 存下來,供下次使用

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(() => {
// do componentDidMount
}, [])

等同 class componentDidUpdate

1
2
3
useEffect(() => {
// do 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;


Hey!想學習更多前端知識嗎?

最近 Lala 開了前端課程 👉【實地掌握RWD - 12小時新手實戰班】👈
無論您是 0 基礎新手,又或是想學 RWD 的初學者,
我們將帶你從零開始,深入了解並掌握 RWD 響應式網頁設計的核心技術,快來一起看看吧 😊



🚀線上課程分享

線上課程可以加速學習的時間,省去了不少看文件的時間XD,以下是我推薦的一些課程
想學習更多關於前後端的線上課程,可以參考看看。

Hahow

Hahow 有各式各樣類型的課程,而且是無限次數觀看,對學生或上班族而言,不用擔心被時間綁住



六角學院

如果你是初學者,非常推薦六角學院哦!
剛開始轉職也是上了六角的課,非常的淺顯易懂,最重要的是,隨時還有線上的助教幫你解決問題!


Udemy

Udemy 裡的課程非常的多,品質普遍不錯,且價格都滿實惠的,CP值很高!
也是很多工程師推薦的線上課程網站。
❤️