
- ReactJS 教程
- ReactJS - 首頁
- ReactJS - 簡介
- ReactJS - 路線圖
- ReactJS - 安裝
- ReactJS - 特性
- ReactJS - 優點與缺點
- ReactJS - 架構
- ReactJS - 建立 React 應用
- ReactJS - JSX
- ReactJS - 元件
- ReactJS - 巢狀元件
- ReactJS - 使用新建立的元件
- ReactJS - 元件集合
- ReactJS - 樣式
- ReactJS - 屬性 (props)
- ReactJS - 使用屬性建立元件
- ReactJS - props 驗證
- ReactJS - 建構函式
- ReactJS - 元件生命週期
- ReactJS - 事件管理
- ReactJS - 建立一個事件感知元件
- ReactJS - 在 Expense Manager 應用中引入事件
- ReactJS - 狀態管理
- ReactJS - 狀態管理 API
- ReactJS - 無狀態元件
- ReactJS - 使用 React Hooks 進行狀態管理
- ReactJS - 使用 React Hooks 進行元件生命週期管理
- ReactJS - 佈局元件
- ReactJS - 分頁
- ReactJS - Material UI
- ReactJS - Http 客戶端程式設計
- ReactJS - 表單程式設計
- ReactJS - 受控元件
- ReactJS - 非受控元件
- ReactJS - Formik
- ReactJS - 條件渲染
- ReactJS - 列表
- ReactJS - Keys
- ReactJS - 路由
- ReactJS - Redux
- ReactJS - 動畫
- ReactJS - Bootstrap
- ReactJS - Map
- ReactJS - 表格
- ReactJS - 使用 Flux 管理狀態
- ReactJS - 測試
- ReactJS - CLI 命令
- ReactJS - 構建和部署
- ReactJS - 例子
- Hooks
- ReactJS - Hook 簡介
- ReactJS - 使用 useState
- ReactJS - 使用 useEffect
- ReactJS - 使用 useContext
- ReactJS - 使用 useRef
- ReactJS - 使用 useReducer
- ReactJS - 使用 useCallback
- ReactJS - 使用 useMemo
- ReactJS - 自定義 Hook
- ReactJS 高階
- ReactJS - 可訪問性
- ReactJS - 程式碼分割
- ReactJS - Context
- ReactJS - 錯誤邊界
- ReactJS - 轉發 Refs
- ReactJS - 片段
- ReactJS - 高階元件
- ReactJS - 與其他庫整合
- ReactJS - 效能最佳化
- ReactJS - Profiler API
- ReactJS - Portals
- ReactJS - 無 ES6 ECMAScript 的 React
- ReactJS - 無 JSX 的 React
- ReactJS - 協調
- ReactJS - Refs 和 DOM
- ReactJS - Render Props
- ReactJS - 靜態型別檢查
- ReactJS - Strict Mode
- ReactJS - Web Components
- 附加概念
- ReactJS - 日期選擇器
- ReactJS - Helmet
- ReactJS - 內聯樣式
- ReactJS - PropTypes
- ReactJS - BrowserRouter
- ReactJS - DOM
- ReactJS - 走馬燈
- ReactJS - 圖示
- ReactJS - 表單元件
- ReactJS - 參考 API
- ReactJS 有用資源
- ReactJS - 快速指南
- ReactJS - 有用資源
- ReactJS - 討論
ReactJS - 自定義 Hook
Hooks 是函式元件不可分割的一部分。它們可以用來增強函式元件的功能。React 提供了一些內建 Hook。儘管內建 Hook 功能強大,可以實現任何功能,但專用 Hook 仍然是必要的,並且需求量很大。
React 瞭解了開發者的這種需求,並允許透過現有的 Hook 建立新的自定義 Hook。開發者可以從函式元件中提取特殊的功能,並將其建立為一個單獨的 Hook,可以在任何函式元件中使用。
讓我們在本節學習如何建立自定義 Hook。
建立自定義 Hook
讓我們建立一個具有無限滾動功能的新 React 函式元件,然後從函式元件中提取無限滾動功能並建立一個自定義 Hook。建立自定義 Hook 後,我們將嘗試更改原始函式元件以使用我們的自定義 Hook。
實現無限滾動功能
元件的基本功能是簡單地透過生成虛擬待辦事項列表來顯示它。當用戶滾動時,元件將生成一組新的虛擬待辦事項列表並將其附加到現有列表。
首先,建立一個新的 React 應用,並使用以下命令啟動它。
create-react-app myapp cd myapp npm start
接下來,在元件資料夾下(src/components/TodoList.js)建立一個 React 元件 TodoList。
function TodoList() { return <div>Todo List</div> } export default TodoList
接下來,更新根元件 App.js 以使用新建立的 TodoList 元件。
import TodoList from './components/TodoList'; function App() { return ( <div style={{ padding: "5px"}}> <TodoList /> </div> ); } export default App;
接下來,建立一個計數狀態變數來維護要生成的待辦事項數量。
const [count, setCount] = useState(100)
這裡:
初始要生成的專案數量為 100
count 是用於維護待辦事項數量的狀態變數
接下來,建立一個數據陣列來維護生成的虛擬待辦事項,並使用 useMemo Hook 保留引用。
React.useMemo(() => { for (let i = 1; i <= count; i++) { data.push({ id: i, todo: "Todo Item " + i.toString() }) } }, [count]);
這裡:
使用 useMemo 來限制每次渲染時待辦事項的生成。
使用 count 作為依賴項,以便在計數更改時重新生成待辦事項
接下來,將處理程式附加到滾動事件,並在使用者移動到頁面末尾時更新要生成的待辦事項計數。
React.useEffect(() => { function handleScroll() { const isBottom = window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight; if (isBottom) { setCount(count + 100) } } window.addEventListener("scroll", handleScroll); return () => { window.removeEventListener("scroll", handleScroll); }; }, [])
這裡我們有:
使用 useEffect Hook 確保 DOM 已準備好附加事件。
為滾動事件附加了一個事件處理程式 scrollHandler。
當元件從應用程式解除安裝時,刪除事件處理程式
在滾動事件處理程式中使用 DOM 屬性查詢使用者是否到達頁面底部。
一旦使用者到達頁面底部,計數就會增加 100
一旦計數狀態變數更改,元件就會重新渲染,並且會載入更多待辦事項。
最後,按如下所示渲染待辦事項:
<div> <p>List of Todo list</p> <ul> {data && data.map((item) => <li key={item.id}>{item.todo}</li> )} </ul> </div>
元件的完整原始碼如下:
import React, { useState } from "react" function TodoList() { const [count, setCount] = useState(100) let data = [] React.useMemo(() => { for (let i = 1; i <= count; i++) { data.push({ id: i, todo: "Todo Item " + i.toString() }) } }, [count]); React.useEffect(() => { function handleScroll() { const isBottom = window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight; if (isBottom) { setCount(count + 100) } } window.addEventListener("scroll", handleScroll); return () => { window.removeEventListener("scroll", handleScroll); }; }, []) return ( <div> <p>List of Todo list</p> <ul> {data && data.map((item) => <li key={item.id}>{item.todo}</li> )} </ul> </div> ) } export default TodoList
接下來,開啟瀏覽器並執行應用程式。應用程式將在使用者到達頁面末尾時附加新的待辦事項,並無限期地繼續,如下所示:

實現useInfiniteScroll Hook
接下來,讓我們嘗試透過從現有元件中提取邏輯來建立新的自定義 Hook,然後在單獨的元件中使用它。建立一個新的自定義 Hook,useInfiniteScroll (src/Hooks/useInfiniteScroll.js),如下所示
import React from "react"; export default function useInfiniteScroll(loadDataFn) { const [bottom, setBottom] = React.useState(false); React.useEffect(() => { window.addEventListener("scroll", handleScroll); return () => { window.removeEventListener("scroll", handleScroll); }; }, []); React.useEffect(() => { if (!bottom) return; loadDataFn(); }, [bottom]); function handleScroll() { if (window.innerHeight + document.documentElement.scrollTop != document.documentElement.offsetHeight) { return; } setBottom(true) } return [bottom, setBottom]; }
這裡我們有:
使用use關鍵字命名自定義 Hook。這是使用use關鍵字作為自定義 Hook 名稱開頭的慣例,它也是 React 的提示,提示該函式是一個自定義 Hook
使用狀態變數 bottom 來了解使用者是否到達頁面底部。
使用 useEffect 在 DOM 可用後註冊滾動事件
使用泛型函式 loadDataFn 在元件中建立 Hook 時提供。這將能夠為載入資料建立自定義邏輯。
在滾動事件處理程式中使用 DOM 屬性來跟蹤使用者滾動位置。當用戶到達頁面底部時,bottom 狀態變數會發生變化。
返回 bottom 狀態變數的當前值 (bottom) 和用於更新 bottom 狀態變數的函式 (setBottom)
接下來,建立一個新元件 TodoListUsingCustomHook 來應用新建立的 Hook。
function TodoListUsingCustomHook() { return <div>Todo List</div> } export default TodoListUsingCustomHook
接下來,更新根元件 App.js 以使用新建立的 TodoListUsingCustomHook 元件。
import TodoListUsingCustomHook from './components/TodoListUsingCustomHook'; function App() { return ( <div style={{ padding: "5px"}}> <TodoListUsingCustomHook /> </div> ); } export default App;
接下來,建立一個狀態變數 data 來管理待辦事項列表。
const [data, setData] = useState([])
接下來,建立一個狀態變數 count 來管理要生成的待辦事項列表的數量。
const [data, setData] = useState([])
接下來,應用無限滾動自定義 Hook。
const [bottom, setBottom] = useInfiniteScroll(loadMoreData)
這裡,bottom 和 setBottom 由 useInfiniteScroll Hook 公開。
接下來,建立用於生成待辦事項列表的函式 loadMoreData
function loadMoreData() { let data = [] for (let i = 1; i <= count; i++) { data.push({ id: i, todo: "Todo Item " + i.toString() }) } setData(data) setCount(count + 100) setBottom(false) }
這裡:
根據計數狀態變數生成待辦事項
將計數狀態變數增加 100
使用 setData 函式將生成的待辦事項列表設定為 data 狀態變數
使用 setBottom 函式將 bottom 狀態變數設定為 false
接下來,生成初始待辦事項,如下所示:
const loadData = () => { let data = [] for (let i = 1; i <= count; i++) { data.push({ id: i, todo: "Todo Item " + i.toString() }) } setData(data) } React.useEffect(() => { loadData(count) }, [])
這裡:
初始待辦事項是根據初始計數狀態變數生成的
使用 setData 函式將生成的待辦事項列表設定為 data 狀態變數
使用 useEffect 確保在 DOM 可用後生成資料
最後,按如下所示渲染待辦事項:
<div> <p>List of Todo list</p> <ul> {data && data.map((item) => <li key={item.id}>{item.todo}</li> )} </ul> </div>
元件的完整原始碼如下所示:
import React, { useState } from "react" import useInfiniteScroll from '../Hooks/useInfiniteScroll' function TodoListUsingCustomHook() { const [data, setData] = useState([]) const [count, setCount] = useState(100) const [bottom, setBottom] = useInfiniteScroll(loadMoreData) const loadData = () => { let data = [] for (let i = 1; i <= count; i++) { data.push({ id: i, todo: "Todo Item " + i.toString() }) } setData(data) } function loadMoreData() { let data = [] for (let i = 1; i <= count; i++) { data.push({ id: i, todo: "Todo Item " + i.toString() }) } setData(data) setCount(count + 100) setBottom(false) } React.useEffect(() => { loadData(count) }, []) return ( <div> <p>List of Todo list</p> <ul> {data && data.map((item) => <li key={item.id}>{item.todo}</li> )} </ul> </div> ) } export default TodoListUsingCustomHook
最後,開啟瀏覽器並檢查輸出。應用程式將在使用者到達頁面末尾時附加新的待辦事項,並無限期地繼續,如下所示:

行為與 TodoList 元件相同。我們已成功地從元件中提取邏輯並將其用於建立我們的第一個自定義 Hook。現在,可以在任何應用程式中使用自定義 Hook。
總結
自定義 Hook 是一種現有的功能,用於將可重用的邏輯與函式元件分離,並允許在不同的函式元件中使其成為真正可重用的 Hook。