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’ }。