什麼是 React Hooks
React Hooks 是 React 在 2019 年推出 16.8 版本的新功能,在此之前要在 React 中修改狀態通常需要透過 class components,這不僅讓程式碼變得複雜,而且可能會導致某些限制,而 Hooks 這個新功能允許我們在不使用 class 的情況下使用狀態以及其他的 React 生命週期。Hooks 提供了一種更直觀的方式來管理元件的狀態和生命週期,使得 function components 可以擁有 side effects、local component state、context features 等功能,而不再依賴於 class components。
Hooks 的引入,不僅讓 component 結構更清晰,也使得重用和組合程式碼變得更為容易。通過使用不同的 Hooks,例如 useState, useEffect, useContext, useReducer 等,開發者可以在 function component 中管理 state、執行 side effects、訂閱外部資料,或者存取 context。這種新的做法讓 function components 變得更加強大,也使得 React 程式碼更易於編寫和維護。
通過這種方式,React Hooks 有助於將複雜的邏輯抽象成更簡單、更可重用的個別函式,並且能夠保持 component 的清晰和易於理解。這也是為什麼 Hooks 很快就被 React 社區廣泛接受,並成為現代 React 開發的重要部分。
接下來介紹常用的 Hooks - useState
:
useState
const [count, setCount] = useState(0)
useState
是 React 提供的一個 hook,可以用來管理狀態,這個 hook 會回傳一個陣列,第一個值是目前的狀態,第二個值是改變狀態的函式,以目前的例子來說,count
是目前的狀態,setCount
是改變狀態的函式,而 useState
的參數是初始值,這邊會設定從 0 開始。而如果要改變狀態的話,可以透過 setCount
來改變,並且當狀態改變時,React 會重新渲染元件。
知道了 useState
的用法之後,就可以來實作一個 Todo List 了。
Todo List API
Todo List 的狀態可以存在 localStorage 中,這樣狀態在重新整理後也不會消失,而這篇文章會使用 超新手入門 Node.js 這篇的範例 API,這個 API 分別提供了以下幾個路徑:
GET /todos
:取得所有待辦事項POST /todos
:新增一個待辦事項PUT /todos/:id
:更新一個待辦事項DELETE /todos
:刪除所有待辦事項DELETE /todos/:id
:刪除一個待辦事項
預計這個 TodoList 會分成三個主要區塊:
- 輸入待辦事項
- 顯示待辦事項的列表
- 顯示待辦事項數量以及刪除所有待辦事項的按鈕
輸入事項
輸入事項的部分,由於需要控制輸入框的狀態,所以會使用到 useState
。
import { useState } from 'react'
function App() {
const [inputText, setInputText] = useState('')
// ...
前面提到 useState
會回傳一個陣列,第一個值是目前的狀態,第二個值是改變狀態的函式,這邊會把目前輸入框的值存在 inputText
中,並且使用 setInputText
來改變 inputText
的值。而 useState
的參數是初始值,這邊會設定為空字串。
接著要把輸入框的值綁定到 inputText
,這邊會使用到 React 的 onChange
事件:
<input
type='text'
placeholder='請輸入事項'
value={inputText}
onChange={(e) => setInputText(e.target.value)}
/>
然後要建立兩個函式來分別呼叫新增事項和取得所有事項的 API,會需要建立一個 todos
的狀態來存放所有的事項:
const [todos, setTodos] = useState([]) // todos 的初始值為空陣列
// 取得所有事項
const fetchTodos = async () => {
try {
const response = await fetch(url, { method: 'get' }) // 發送 GET 請求
const data = await response.json() // 取得回傳的資料
setTodos(data) // 把取得的資料存放到 todos 中
} catch (err) {
console.log(err)
}
}
// 新增事項
const addTodo = async () => {
try {
await fetch(url, {
method: 'post', // 發送 POST 請求
body: JSON.stringify({ title: inputText }), // 設定請求的內容
headers: {
'Content-Type': 'application/json' // 設定請求的資料類型為 JSON
}
})
fetchTodos() // 新增成功後重新取得所有事項
setInputText('') // 清空輸入框
} catch (err) {
console.log(err)
}
}
url 的部分是來自於 超新手入門 Node.js 這篇的範例 API。
在每次新增事項後,會呼叫 fetchTodos
來重新取得所有事項,並且清空輸入框的值。
接下來要把新增事項的按鈕綁定到 addTodo
:
<button type='button' onClick={addTodo}>
新增
</button>
顯示事項列表
顯示事項列表的部分,會使用到 React 的 useEffect
來在元件載入時取得所有事項:
import { useState, useEffect } from 'react'
useEffect(() => {
fetchTodos()
}, []) // 第二個參數為空陣列,代表只會在元件載入時執行
設定完 useEffect
之後,就可以在 todos
中取得所有事項,並且使用 map
來把每個事項渲染到畫面上:
<ul>
{todos.map((item) => {
return (
<li key={item._id}>
<label>
<input
type='checkbox'
checked={item.completed}
onChange={() => updateTodo(item._id, !item.completed)}
/>
<span>{item.title}</span>
</label>
<button type='button' onClick={() => deleteTodo(item._id)}>
刪除
</button>
</li>
)
})}
</ul>
在使用 map
時,要記得要加上 key
,這樣 React 才能夠正確的辨識每個元素,並且在更新時能夠正確的比對。而且 key
必須是唯一的,所以這邊使用 _id
來當作 key
。
這裡可以看到有一個刪除單一事項的按鈕,這個按鈕會呼叫 deleteTodo
:
const deleteTodo = async (id) => {
try {
await fetch(`${url}/${id}`, { method: 'delete' }) // 發送 DELETE 請求,並且帶上 id
fetchTodos()
} catch (err) {
console.log(err)
}
}
顯示待辦事項數量以及刪除所有待辦事項的按鈕
刪除全部的事項由於有一個專屬的刪除 API,所以會單獨寫一個函式來呼叫:
const deleteAllTodos = async () => {
try {
await fetch(url, { method: 'delete' })
fetchTodos()
} catch (err) {
console.log(err)
}
}
最後把這個刪除的函式綁定到按鈕上,並且顯示目前有多少待辦事項:
<div>
<p>目前一共有 {todos.length} 筆任務</p>
<button
type='button'
onClick={deleteAllTodos}
>
全部清除
</button>
</div>
操作起來的話,就會像這樣:
以上示範了如何使用 React 的 hooks 來串接 API 並實作一個 Todo List,如果想要了解更多有關 React 的最新資訊以及業界的最佳實踐,可以參考 React 全攻略 - 入門到進階。