切换主题
React 内置 Hook
字数
1483 字
阅读时间
7 分钟
请查看 Hooks 目录 了解详细内容。
State Hook
存储状态变量值。
useState
最基础的 Hook,用于组件内声明状态变量。
基础语法
ts
const [state, setState] = useState(initialState)
- setState 可接收:
- 新值:
setState(newValue)
- 更新函数:
setState(prev => newValue)
- 新值:
useReducer
用于处理复杂状态逻辑的 Hook。
基础语法
ts
const [state, dispatch] = useReducer(reducer, initialState)
- reducer 函数接收 (state, action) 返回新状态
- 通过 dispatch(action) 触发状态更新
使用示例
ts
// 定义 reducer 函数
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return [...state, {
id: action.id,
text: action.text,
completed: false
}]
case 'TOGGLE_TODO':
return state.map(todo =>
todo.id === action.id
? { ...todo, completed: !todo.completed }
: todo
)
default:
return state
}
}
// 在组件中使用
function TodoList() {
const [todos, dispatch] = useReducer(todoReducer, [])
const addTodo = (text) => {
dispatch({
type: 'ADD_TODO',
id: Date.now(),
text
})
}
const toggleTodo = (id) => {
dispatch({
type: 'TOGGLE_TODO',
id
})
}
}
适用场景
- 状态逻辑复杂
- 状态之间有依赖关系
- 需要集中管理状态更新逻辑
reducer 的本质
reducer 是一个自定义的状态管理器,让开发者完全控制状态的变化逻辑。
与 useState 的对比:
- useState:直接设置新状态
- useReducer:通过预定义的 reducer 函数计算新状态
优势:
- 状态更新逻辑更集中,易于维护
- 可以处理复杂的状态依赖关系
- 便于测试(reducer 是纯函数)
- 方便状态逻辑的复用
Context Hook
上下文 Hook 允许从祖先组件接收信息,而无需将其作为 props 传递。
useContext
基础用法
tsx
// 1. 创建 Context
const ThemeContext = createContext('light')
// 2. 提供 Context
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedButton />
</ThemeContext.Provider>
)
}
// 3. 消费 Context
function ThemedButton() {
const theme = useContext(ThemeContext)
return <button className={theme}>按钮</button>
}
实际应用示例
tsx
// 用户信息 Context
const UserContext = createContext({
user: null,
login: () => {},
logout: () => {}
})
// Provider 组件
function UserProvider({ children }) {
const [user, setUser] = useState(null)
const login = (userData) => {
setUser(userData)
}
const logout = () => {
setUser(null)
}
return (
<UserContext.Provider value={{ user, login, logout }}>
{children}
</UserContext.Provider>
)
}
// 在需要用户信息的组件中使用
function UserProfile() {
const { user, logout } = useContext(UserContext)
if (!user) return <div>请登录</div>
return (
<div>
<h2>{user.name}</h2>
<button onClick={logout}>退出登录</button>
</div>
)
}
// 应用入口
function App() {
return (
<UserProvider>
<UserProfile />
</UserProvider>
)
}
使用场景
- 全局主题配置
- 用户认证信息
- 语言偏好设置
- 全局状态管理
- 共享数据或函数
注意事项
Context 适用于需要在组件树中广泛共享的数据。过度使用 Context 可能导致组件复用性降低。因此,对于局部状态,推荐使用 props 传递或状态提升。
Context vs 状态管理库(valtio、zustand等)的区别
Context
- 是 React 原生的依赖注入机制
- 主要用于传递数据,不包含状态管理功能
- 每次 Context 值变化,所有消费该 Context 的组件都会重新渲染
- 适用于:
- 静态数据或低频更新的数据
- 主题、语言等全局配置
- 较小规模的应用
Valtio
- 基于 Proxy 的响应式状态管理
- 可以像普通对象一样直接修改状态
- 精确更新,只有使用到的属性变化才会触发重渲染
- 适用于:
- 需要频繁更新的复杂状态
- 对性能要求较高的场景
ts// Valtio 示例 const state = proxy({ count: 0 }) state.count++ // 直接修改
Zustand
- 极简的状态管理库
- 基于 hook 的 API 设计
- 支持状态分片,可以只订阅部分状态
- 适用于:
- 中大型应用的状态管理
- 需要良好性能和可维护性的场景
ts// Zustand 示例 const useStore = create((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })) }))
选择建议:
- 简单的全局配置 → Context
- 复杂的状态管理 → Zustand/Valtio
- 需要响应式特性 → Valtio
- 需要更好的性能和扩展性 → Zustand
Ref Hook
允许组件保存一些不用于渲染的信息:
- 希望组件"记住"某些信息,但又不想让这些信息触发新的渲染
useRef 使用场景
- 存储 DOM 元素引用
tsx
function VideoPlayer() {
const videoRef = useRef(null)
const play = () => {
videoRef.current.play()
}
const pause = () => {
videoRef.current.pause()
}
return (
<div>
<video ref={videoRef}>
<source src="video.mp4" type="video/mp4" />
</video>
<button onClick={play}>播放</button>
<button onClick={pause}>暂停</button>
</div>
)
}
- 保存定时器 ID
tsx
function AutoSave({ onSave }) {
const saveTimerRef = useRef(null)
useEffect(() => {
// 每 5 秒自动保存
saveTimerRef.current = setInterval(() => {
onSave()
}, 5000)
return () => {
// 清理定时器
clearInterval(saveTimerRef.current)
}
}, [onSave])
return <div>自动保存中...</div>
}
- 记录前一次的值
tsx
function MessageList() {
const [messages, setMessages] = useState([])
const prevMessagesRef = useRef([])
useEffect(() => {
// 保存前一次的消息列表
prevMessagesRef.current = messages
}, [messages])
const hasNewMessages = messages.length > prevMessagesRef.current.length
return (
<div>
{hasNewMessages && <div>有新消息!</div>}
{/* 消息列表渲染 */}
</div>
)
}
- 存储可变值(不触发重渲染)
tsx
function StopWatch() {
const [time, setTime] = useState(0)
const intervalRef = useRef(null)
const isRunningRef = useRef(false) // 不需要触发重渲染的状态
const start = () => {
if (isRunningRef.current) return
isRunningRef.current = true
intervalRef.current = setInterval(() => {
setTime(t => t + 1)
}, 1000)
}
const stop = () => {
isRunningRef.current = false
clearInterval(intervalRef.current)
}
return (
<div>
<div>时间: {time}秒</div>
<button onClick={start}>开始</button>
<button onClick={stop}>停止</button>
</div>
)
}
useRef 特点
- ref.current 的变化不会触发组件重新渲染
- ref 值在组件的整个生命周期内保持不变
- 适合存储那些不需要参与 UI 渲染的值
- 常用于存储 DOM 引用、定时器 ID、前一次的值等
使用建议
- 如果值需要参与渲染,使用 useState
- 如果值仅用于存储/记忆且不需要触发重渲染,使用 useRef
- 不要在渲染期间读取或写入 ref.current