【React】打卡笔记,入门学习03:useState、useEffect、useRef、useMemo
系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 系列文章目录
- 前言
- 一、`useState` - 状态管理
- 二、`useRef` - 引用对象
- 三、`useEffect` - 副作用处理
- 四、`useMemo` - 缓存 “计算结果”
- 五、`useCallback` - 缓存 “函数”
- 总结
前言
提示:这里可以添加本文要记录的大概内容:
苦学react第二周,2025年11月7日14:22:49
提示:以下是本篇文章正文内容,下面案例可供参考
一、useState - 状态管理
作用:在函数组件中管理状态,触发重新渲染。
就是vue的data变量
但是赋值方法跟微信原生小程序一样要用set,定义变量时,前面的是变量名,后面的的赋值方法
// 基本用法
const [state, setState] = useState(initialValue);// 代码中的例子
const [queryParams, setQueryParams] = useState({paymentTime: 'all',dateRange: undefined,// ...
});const [transactions, setTransactions] = useState<Transaction[]>([]);
const [activeIds, setActiveIds] = useState<Set<string>>(new Set());
- 返回 [
当前值,更新函数] - 调用
setState会触发组件重新渲染 - 可以传入初始值或函数
二、useRef - 引用对象
作用:创建一个可变的 ref 对象,在组件生命周期内保持引用不变,且更新不会触发重新渲染。
主要用于两种场景:
获取 DOM 元素或组件实例;
存储跨组件渲染周期的 “持久化数据”(修改不会触发组件重渲染)。
语法:const ref = useRef(initialValue)
返回一个ref对象,其current属性用于存储值(初始值为initialValue)
// 基本用法
const ref = useRef(initialValue);// 代码中的例子
const tableRef = useRef<DataTableHandle>(null);
.current可读写- 更新
.current不会触发重新渲染
一般用于:1、保存 DOM 引用; 2、保存子组件实例引用;3、存可变值(如定时器 ID)
// 引入子组件
import DataTable, { type DataTableHandle } from '@/components/table';// DataTable ref
const tableRef = useRef<DataTableHandle>(null);// 通过 ref 调用子组件的方法
tableRef.current?.reset(); // 重置表格到第一页
tableRef.current?.reload(); // 重新加载数据// 页面使用挂载<DataTableref={tableRef}data={dataList}
/>
三、useEffect - 副作用处理
作用:处理组件的 “副作用”(即不属于组件渲染逻辑的操作),例如数据请求、订阅事件、手动修改 DOM、定时器等
语法:useEffect(effectCallback, dependencies)
通俗说就是,既可以当watch监听,又可以当onMounted用
监听器用法如下,后面的数组可以为空,传值则表示任意一值变化就执行
// 当筛选条件变化时,重置分页到第一页useEffect(() => {tableRef.current?.reset();}, [queryParams.paymentTime,queryParams.dateRange,queryParams.paymentType,queryParams.accountNumber,queryParams.keyword,]);
当做接口请求时,为空就好,就相当于onMounted了。
useEffect(() => {getCategoryList();}, []);
四、useMemo - 缓存 “计算结果”
缓存计算结果,仅在依赖项变化时重新计算。
这玩意和vue的computed很像,这里我贴出对比代码
// Vue 3 Composition API
import { computed, ref } from 'vue'const firstName = ref('张')
const lastName = ref('三')// computed 会自动追踪依赖
const fullName = computed(() => {return firstName.value + lastName.value
})// 使用:像普通属性一样访问
console.log(fullName.value) // "张三"
// React
import { useMemo, useState } from 'react'const [firstName, setFirstName] = useState('张')
const [lastName, setLastName] = useState('三')// useMemo 需要手动指定依赖
const fullName = useMemo(() => {return firstName + lastName
}, [firstName, lastName]) // 手动指定依赖数组// 使用:直接使用值
console.log(fullName) // "张三"
两者都会在依赖项不变时返回缓存值,避免重复计算。
不同点在于,vue写computed,直接使用参数即可,但是react需要在后面传入,有点类似于useEffect后面那个数组
// Vue 会自动追踪函数内部使用的响应式变量
const total = computed(() => {return items.value.reduce((sum, item) => sum + item.price, 0)// ↑ Vue 自动知道依赖 items
})// React 需要手动列出所有依赖
const total = useMemo(() => {return items.reduce((sum, item) => sum + item.price, 0)
}, [items]) // 必须手动指定依赖
五、useCallback - 缓存 “函数”
说实话这个我有点难理解,按我的想法是,父级列表,在使用子级item组件时。需要传入一些点击事件方法,这些方法就需要加useCallback。
下方是示例代码
// 子组件:TodoItem.tsx
import { memo } from 'react';interface TodoItemProps {id: string;text: string;completed: boolean;onToggle: (id: string) => void; // ← 接收函数onDelete: (id: string) => void; // ← 接收函数
}// 使用 memo 优化,只有 props 变化时才重新渲染
const TodoItem = memo(({ id, text, completed, onToggle, onDelete }: TodoItemProps) => {console.log(`TodoItem ${id} 渲染了`); // 用于调试return (<div className="flex items-center gap-2"><inputtype="checkbox"checked={completed}onChange={() => onToggle(id)}/><span className={completed ? 'line-through' : ''}>{text}</span><button onClick={() => onDelete(id)}>删除</button></div>);
});export default TodoItem;
分割线
// 父组件:TodoList.tsx
import { useState, useCallback, useMemo } from 'react';
import TodoItem from './TodoItem';interface Todo {id: string;text: string;completed: boolean;
}export default function TodoList() {const [todos, setTodos] = useState<Todo[]>([{ id: '1', text: '学习 React', completed: false },{ id: '2', text: '学习 Vue', completed: false },{ id: '3', text: '学习 TypeScript', completed: true },]);const [filter, setFilter] = useState<'all' | 'active' | 'completed'>('all');const [newTodo, setNewTodo] = useState('');// ❌ 不用 useCallback - 每次渲染都创建新函数// const handleToggle = (id: string) => {// setTodos(prev => prev.map(todo => // todo.id === id ? { ...todo, completed: !todo.completed } : todo// ));// };// ✅ 使用 useCallback - 函数引用保持稳定const handleToggle = useCallback((id: string) => {setTodos(prev => prev.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo));}, []); // 空依赖数组,函数永远不变const handleDelete = useCallback((id: string) => {setTodos(prev => prev.filter(todo => todo.id !== id));}, []);const handleAdd = useCallback(() => {if (newTodo.trim()) {setTodos(prev => [...prev, {id: Date.now().toString(),text: newTodo,completed: false,}]);setNewTodo('');}}, [newTodo]); // 依赖 newTodo// 过滤后的列表(使用 useMemo)const filteredTodos = useMemo(() => {if (filter === 'active') return todos.filter(t => !t.completed);if (filter === 'completed') return todos.filter(t => t.completed);return todos;}, [todos, filter]);return (<div className="p-4"><h1>待办事项列表</h1>{/* 筛选器 */}<div className="mb-4"><button onClick={() => setFilter('all')}>全部</button><button onClick={() => setFilter('active')}>未完成</button><button onClick={() => setFilter('completed')}>已完成</button></div>{/* 添加新待办 */}<div className="mb-4"><inputvalue={newTodo}onChange={(e) => setNewTodo(e.target.value)}placeholder="输入新待办..."/><button onClick={handleAdd}>添加</button></div>{/* 待办列表 */}<div>{filteredTodos.map(todo => (<TodoItemkey={todo.id}id={todo.id}text={todo.text}completed={todo.completed}onToggle={handleToggle} // ← 使用 useCallback 的函数onDelete={handleDelete} // ← 使用 useCallback 的函数/>))}</div></div>);
}
性能对比
不用 useCallback:
// ❌ 每次父组件渲染,handleToggle 都是新函数
const handleToggle = (id: string) => { ... };// 结果:
// - 父组件渲染 → handleToggle 是新函数引用
// - TodoItem 的 props 变化(函数引用变了)
// - TodoItem 重新渲染(即使数据没变)
// - 所有 TodoItem 都会重新渲染!
使用 useCallback:
// ✅ 函数引用保持稳定
const handleToggle = useCallback((id: string) => { ... }, []);// 结果:
// - 父组件渲染 → handleToggle 是同一个函数引用
// - TodoItem 的 props 没变化(函数引用相同)
// - TodoItem 不重新渲染(memo 生效)
// - 只有数据真正变化的 TodoItem 才重新渲染!
总结
提示:这里对文章进行总结:
这些 Hooks 分别解决了函数组件的状态管理(useState)、副作用处理(useEffect)、DOM 操作 / 持久化数据(useRef)、计算结果缓存(useMemo)、函数缓存(useCallback)问题,是 React 函数组件开发的核心工具。
