React Hooks
React Hooks 是 React 16.8 引入的功能,允许函数组件使用状态(state)、生命周期等特性,无需编写类组件。Hooks 使代码更简洁、可复用,并提升了函数组件的灵活性。以下是对 React Hooks 的中文讲解,涵盖核心概念、常用 Hooks、用法示例和注意事项,力求简洁清晰。
1. 什么是 React Hooks?
Hooks 是特殊的函数,允许在函数组件中“钩入” React 的状态管理和生命周期功能。主要目标:
- 取代类组件的复杂逻辑(如
setState
、componentDidMount
)。 - 提高代码复用性,避免高阶组件(HOC)或渲染属性(render props)的嵌套。
- 使函数组件更强大,逻辑更清晰。
注意:Hooks 仅适用于函数组件和自定义 Hooks,不支持类组件。
2. 常用内置 Hooks
React 提供了一系列内置 Hooks,以下是常用的核心 Hooks:
1. useState
管理组件状态,替代类组件的 this.state
和 this.setState
。
示例
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // 初始值为 0
return (
<div>
<p>计数: {count}</p>
<button onClick={() => setCount(count + 1)}>加 1</button>
</div>
);
}
- 说明:
useState
返回状态值(count
)和更新函数(setCount
)。- 更新状态时,
setCount
可接收新值或函数(如setCount(prev => prev + 1)
)。 - 用途:管理简单状态,如计数器、表单输入等。
2. useEffect
处理副作用(如数据获取、订阅、DOM 操作),替代类组件的生命周期方法(componentDidMount
、componentDidUpdate
、componentWillUnmount
)。
示例
import { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(res => res.json())
.then(setData);
// 清理函数(可选)
return () => console.log('清理副作用');
}, []); // 空依赖数组,类似 componentDidMount
return <div>{data ? data.name : '加载中...'}</div>;
}
- 说明:
useEffect
接受两个参数:副作用函数和依赖数组。- 依赖数组(
[]
)控制副作用执行时机:- 空数组(
[]
):仅在挂载时执行一次。 - 含变量(如
[count]
):变量变化时执行。 - 无依赖数组:每次渲染都执行。
- 空数组(
- 返回清理函数(可选),在组件卸载或依赖变化前执行。
- 用途:数据获取、事件监听、定时器等。
3. useContext
访问 React Context,简化跨组件传递数据。
示例
import { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function ThemeComponent() {
const theme = useContext(ThemeContext);
return <div>当前主题: {theme}</div>;
}
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemeComponent />
</ThemeContext.Provider>
);
}
- 说明:
useContext
获取最近的Provider
提供的value
。 - 用途:主题切换、全局用户状态等。
4. useReducer
管理复杂状态逻辑,类似 Redux 的 reducer,适合替代 useState
处理复杂状态更新。
示例
import { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>计数: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>加 1</button>
<button onClick={() => dispatch({ type: 'decrement' })}>减 1</button>
</div>
);
}
- 说明:
useReducer
返回状态和dispatch
函数,通过 action 更新状态。 - 用途:复杂状态管理,如表单、多状态交互。
5. useCallback
和 useMemo
优化性能,防止不必要的函数或值重新创建。
useCallback
示例
import { useState, useCallback } from 'react';
function Child({ onClick }) {
console.log('Child 渲染');
return <button onClick={onClick}>子组件按钮</button>;
}
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => console.log('点击'), []); // 缓存函数
return (
<div>
<button onClick={() => setCount(count + 1)}>计数: {count}</button>
<Child onClick={handleClick} />
</div>
);
}
- 说明:
useCallback
缓存函数引用,防止子组件因函数重新创建而重新渲染。 - 用途:传递给
React.memo
包裹的子组件。
useMemo
示例
import { useMemo, useState } from 'react';
function ExpensiveComponent({ numbers }) {
const sum = useMemo(() => {
console.log('计算总和');
return numbers.reduce((a, b) => a + b, 0);
}, [numbers]);
return <div>总和: {sum}</div>;
}
- 说明:
useMemo
缓存计算结果,仅在依赖变化时重新计算。 - 用途:昂贵计算、复杂对象生成。
6. useRef
创建可变引用对象,常用于访问 DOM 或保存值(不触发重新渲染)。
示例
import { useRef } from 'react';
function InputFocus() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} />
<button onClick={focusInput}>聚焦输入框</button>
</div>
);
}
- 说明:
useRef
的.current
属性保存引用,值变化不触发渲染。 - 用途:DOM 操作、保存上一次状态、定时器 ID 等。
7. useNavigate
(React Router)
用于编程式导航,来自 react-router-dom
。
示例
import { useNavigate } from 'react-router-dom';
function LoginButton() {
const navigate = useNavigate();
return (
<button onClick={() => navigate('/dashboard')}>
跳转到仪表盘
</button>
);
}
- 说明:
useNavigate
返回导航函数,支持跳转、历史记录操作。 - 用途:动态路由跳转。
3. 自定义 Hooks
将逻辑封装为可复用函数,名称以 use
开头。
示例
import { useState, useEffect } from 'react';
function useWindowSize() {
const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });
useEffect(() => {
const handleResize = () => setSize({ width: window.innerWidth, height: window.innerHeight });
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
function App() {
const { width, height } = useWindowSize();
return <div>窗口大小: {width} x {height}</div>;
}
- 说明:自定义 Hooks 封装状态和副作用逻辑,可在多个组件复用。
- 用途:表单处理、数据获取、动画等。
4. Hooks 规则
React 对 Hooks 使用有严格规则:
- 仅在顶层调用:不要在循环、条件或嵌套函数中调用 Hooks。
- 仅在函数组件或自定义 Hooks 中调用:不要在普通函数或类组件中使用。
- 使用 ESLint 插件(如
eslint-plugin-react-hooks
)检查规则合规性。
示例(错误用法)
function App() {
if (true) {
const [count, setCount] = useState(0); // 错误:在条件中调用
}
}
5. 与 React Router 结合
Hooks 常与 react-router-dom
结合,用于导航、参数获取等:
useParams
:获取动态路由参数(如/users/:id
)。useLocation
:获取当前 URL 信息。useNavigate
:编程式导航。
示例
import { useParams, useNavigate } from 'react-router-dom';
function UserProfile() {
const { id } = useParams(); // 获取 :id 参数
const navigate = useNavigate();
return (
<div>
<h1>用户 ID: {id}</h1>
<button onClick={() => navigate('/')}>返回首页</button>
</div>
);
}
6. 注意事项
- 性能优化:
- 使用
useCallback
和useMemo
避免不必要的重新渲染。 - 谨慎使用
useEffect
,确保依赖数组正确,避免无限循环。
- 调试:
- 使用 React Developer Tools 检查 Hooks 状态。
- 打印依赖数组变化,排查副作用问题。
- 学习曲线:
- 熟悉
useEffect
的依赖管理是关键。 - 自定义 Hooks 需要清晰的逻辑封装。
- 与类组件对比:
- Hooks 代码更简洁,但逻辑分散可能增加维护成本。
- 类组件仍适用于某些场景(如复杂生命周期逻辑)。
7. 选择建议
- 简单状态:使用
useState
。 - 复杂状态:使用
useReducer
。 - 副作用:使用
useEffect
。 - 性能优化:结合
useCallback
、useMemo
和React.memo
。 - 全局数据:使用
useContext
。 - 可复用逻辑:编写自定义 Hooks。
8. 资源
- React 官方文档:https://react.dev/learn#using-hooks
- Hooks API:https://react.dev/reference/react
- 中文社区:搜索“React Hooks 中文教程”或参考掘金、知乎。
如果需要更详细的某 Hook 讲解(如 useEffect
高级用法或自定义 Hooks 案例),请告诉我!