React hook1:useEffect
在編程中,副作用是指函數或表達式在執行過程中對外部環境產生影響的行為。例如:
- 修改外部變量(如全局變量、DOM、API 請求、設置定時器等)
什么是純函數?
// 純函數:輸入相同,輸出必定相同,不影響外部
function add(a, b) {return a + b;
}
//有副作用的函數:影響外部狀態
function updateDOM() {document.title = "Changed!"; //副作用:修改DOM
}
React 推崇函數式編程,核心思想是:
- 組件應該是純函數(給定相同的props和state,渲染相同的 UI)
- 副作用應該被隔離,不能直接在渲染過程中執行
React 如何處理副作用?渲染和外部處理相分離。
由于渲染必須是純的,React 提供了useEffect , 讓副作用在渲染完成后執行,而不是在渲染期間執行。
useEffect是 React 提供的副作用隔離機制,讓不純的操作(如API請求、DOM修改)不影響組件的純渲染邏輯,同時提供清理和優化能力。
- 保持渲染純凈(在組件渲染到屏幕之后運行)
- 避免阻塞渲染
- 自動清理副作用(可以返回一個清理函數,在組件卸載時執行)
react jsx title="useEffect基礎用法"
import { useEffect } from 'react';function Example() {useEffect(() => {console.log('組件首次渲染后執行');}, []); // 空依賴數組表示只運行一次return <div>組件內容</div>;
}
react jsx title="useEffect引入依賴項監聽變化"
function Example({ id }) {useEffect(() => {console.log('id 變化時執行:', id);}, [id]); // 僅在 id 變化時重新執行return <div>ID: {id}</div>;
}
react jsx title="useEffect清理副作用"
function Example() {useEffect(() => {const timer = setInterval(() => {console.log('定時器運行中...');}, 1000);return () => {clearInterval(timer); // 組件卸載時清理定時器};}, []);return <div>查看控制臺</div>;
}
React hook2:useRef
useRef用于直接操作 DOM或存儲可變值(不會觸發重新渲染)。
用法1:操作DOM
import { useRef } from 'react';function InputFocus() {const inputRef = useRef(null); // 1. 創建 refconst handleClick = () => {inputRef.current.focus(); // 3. 通過 .current 操作 DOM};return (<div>{/* 2. 綁定 ref 到元素 */}<input ref={inputRef} type="text" /><button onClick={handleClick}>聚焦輸入框</button></div>);
}
用法2:存儲可變值,不觸發重新渲染
import { useRef, useState } from 'react';function Counter() {const [count, setCount] = useState(0);const renderCount = useRef(0); // 存儲組件渲染次數(不會觸發更新)renderCount.current += 1; // 修改值(不會導致重新渲染)return (<div><p>Count: {count}</p><p>組件渲染次數: {renderCount.current}</p><button onClick={() => setCount(count + 1)}>增加</button></div>);
}
useRef的底層機制
// 近似實現(React 內部簡化邏輯)
function useRef(initialValue) {return { current: initialValue }; // 每次都返回同一個對象!
}
- 存儲原理:React 在組件首次渲染時創建這個{ current: … }對象,后續所有渲染都返回同一個對象(引用不變)。
- 修改current:直接修改current屬性不會觸發渲染,因為對象本身的引用沒變,React 不會檢測到變化。
為什么操作 DOM 需要綁定到標簽?
<input ref={inputRef} />
React 的隱藏邏輯:當ref屬性綁定到 DOM 標簽時,React 會在渲染結束后自動將 DOM 節點賦值給inputRef.current。
// React 內部類似這樣處理
const domNode = document.createElement('input');
inputRef.current = domNode; // 自動填充 DOM 節點
存儲變量為何不需要綁定?
const renderCount = useRef(0);
renderCount.current += 1; // 直接修改即可
存儲變量時,current只是一個普通屬性,你可以自由讀寫,React 不會干涉它的值。
關鍵區別總結
場景 | 是否需要綁定到標簽 | current的值來源 |
---|---|---|
操作 DOM | 需要 | 由 React 自動填充為 DOM 節點 |
存儲可變值 | 不需要 | 由開發者手動賦值/修改 |
React hook3:useMemo
useMemo用于緩存計算結果,只有當依賴項變化時才重新計算,避免重復渲染時不必要的計算開銷。
function UserList({ users, searchTerm }) {// 緩存過濾后的用戶列表,只有當users或searchTerm變化時才重新過濾const filteredUsers = useMemo(() => {return users.filter(user => user.name.toLowerCase().includes(searchTerm.toLowerCase()));}, [users, searchTerm]);return (<ul>{filteredUsers.map(user => (<li key={user.id}>{user.name}</li>))}</ul>);
}
React hook4:useNavigate
**useNavigate 就像一個提供多種導航工具的“工具箱”,可以從中選擇需要的功能來完成路由跳轉、前進/后退、傳遞數據等操作。**用“工具箱”類比 useNavigate:
工具箱里的工具 | 對應 useNavigate 的功能 |
---|---|
導航地圖 | navigate(‘/path’) —— 直接跳轉到指定路徑。 |
橡皮擦 | navigate(‘/path’, { replace: true }) —— 替換當前路由(擦掉當前記錄,不保留歷史)。 |
時間機器按鈕 | navigate(1) 或 navigate(-1) —— 前進或后退到歷史記錄中的頁面。 |
快遞包裹 | navigate(‘/path’, { state: { data } }) —— 跳轉時攜帶隱藏數據(偷偷塞個包裹給下個頁面)。 |
React hook5:useParams()
useParams從當前 URL 路徑中提取參數。
示例代碼:
<Route path="/user/:id" element={<UserProfile />} />
import { useParams } from 'react-router-dom';function UserProfile() {// 提取 URL 中的動態參數(比如 :id)const params = useParams();console.log(params.id); // 訪問 /user/123 時,輸出 "123"return <h1>用戶ID: {params.id}</h1>;
}
當用戶訪問 /user/123 時,useParams() 會提取出 { id: ‘123’ }。