文章目录
一文讲清楚React Hooks
一、什么是 React Hooks?
React Hooks 是 React 16.8 版本引入的新特性,它允许开发者在不编写类组件的情况下使用状态(state)和其他 React 特性(如生命周期、上下文等)。Hooks 的出现解决了类组件中存在的代码复用困难、逻辑分散、生命周期函数臃肿等问题,让函数组件拥有了与类组件同等的能力。
二、常用基础 Hooks
2.1 useState:状态管理
`useState` 是最基础的 Hook,用于在函数组件中添加状态管理。
基本用法
import { useState } from 'react';
function Counter() {
// 声明一个名为count的状态变量,初始值为0
// setCount是更新count的函数
const [count, setCount] = useState(0);
return (
<div>
<p>你点击了 {count} 次</p>
<button onClick={() => setCount(count + 1)}>
点击我
</button>
</div>
);
}
特点
- 返回值:返回一个数组,第一个元素是当前状态值,第二个元素是更新状态的函数
- 状态更新:
- 调用更新函数会重新渲染组件
- 状态更新是异步的,多次连续更新会被合并
- 如果新状态依赖于旧状态,应使用函数形式:`setCount(prevCount => prevCount + 1)`
- 初始化:初始值只在组件首次渲染时生效
2.2 useEffect:副作用处理
`useEffect` 用于处理组件中的副作用操作,如数据获取、订阅、DOM 操作等,相当于类组件中的 `componentDidMount`、`componentDidUpdate` 和 `componentWillUnmount` 的组合。
基本用法
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
// 副作用函数
useEffect(() => {
// 获取用户数据(副作用操作)
const fetchUser = async () => {
const response = await fetch(\`/api/users/\${userId}\`);
const data = await response.json();
setUser(data);
};
fetchUser();
// 清理函数(可选)
return () => {
// 组件卸载或userId变化时执行,用于取消订阅、清除定时器等
console.log('清理操作');
};
}, [userId]); // 依赖数组:只有userId变化时,才会重新执行副作用
if (!user) return <div>Loading...</div>;
return <div>{user.name}</div>;
}
依赖数组说明
- 空数组 `[]`:副作用只在组件挂载时执行一次,清理函数在组件卸载时执行(类似 `componentDidMount` 和 `componentWillUnmount`)
- 包含特定值 `[value1, value2]`:组件挂载时执行,且当数组中的值发生变化时重新执行(类似 `componentDidUpdate`)
- 不提供依赖数组:每次组件渲染都会执行副作用
2.3 useContext:上下文共享
`useContext` 用于在函数组件中获取上下文(Context)的值,避免了通过 props 层层传递数据的麻烦。
基本用法
import { createContext, useContext } from 'react';
// 1. 创建上下文
const ThemeContext = createContext('light');
// 2. 提供上下文值
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedComponent />
</ThemeContext.Provider>
);
}
// 3. 消费上下文值
function ThemedComponent() {
// 使用useContext获取上下文值
const theme = useContext(ThemeContext);
return <div>当前主题:{theme}</div>;
}
特点
- 当 `ThemeContext.Provider` 的 `value` 发生变化时,所有使用 `useContext(ThemeContext)` 的组件都会重新渲染
- `useContext` 接收一个上下文对象(由 `createContext` 创建),并返回该上下文的当前值
三、其他常用 Hooks
3.1 useReducer:复杂状态管理
`useReducer` 是 `useState` 的替代方案,适用于处理复杂状态逻辑或多个子值组成的状态对象。
import { useReducer } from 'react';
// 1. 定义reducer函数:接收当前状态和动作,返回新状态
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return [...state, { id: Date.now(), text: action.text, done: false }];
case 'TOGGLE_TODO':
return state.map(todo =>
todo.id === action.id ? { ...todo, done: !todo.done } : todo
);
default:
return state;
}
}
function TodoList() {
// 2. 使用useReducer:接收reducer和初始状态,返回当前状态和dispatch函数
const [todos, dispatch] = useReducer(todoReducer, []);
return (
<div>
<button onClick={() => dispatch({ type: 'ADD_TODO', text: '新任务' })}>
添加任务
</button>
<ul>
{todos.map(todo => (
<li
key={todo.id}
style={{ textDecoration: todo.done ? 'line-through' : 'none' }}
onClick={() => dispatch({ type: 'TOGGLE_TODO', id: todo.id })}
>
{todo.text}
</li>
))}
</ul>
</div>
);
}
3.2 useCallback:记忆化回调函数
`useCallback` 用于记忆化回调函数,避免在每次渲染时创建新的函数实例,从而优化子组件的性能(配合 `React.memo` 使用)。
import { useCallback, useState } from 'react';
function Parent() {
const [count, setCount] = useState(0);
// 记忆化回调函数:只有当依赖项变化时,才会创建新的函数
const handleClick = useCallback(() => {
console.log('点击事件');
}, []); // 空依赖数组:函数只会被创建一次
return (
<div>
<Child onClick={handleClick} />
<button onClick={() => setCount(count + 1)}>计数:{count}</button>
</div>
);
}
// 使用React.memo优化子组件,避免不必要的重渲染
const Child = React.memo(({ onClick }) => {
console.log('Child 渲染');
return <button onClick={onClick}>点击我</button>;
});
3.3 useMemo:记忆化计算结果
`useMemo` 用于记忆化计算结果,避免在每次渲染时重复执行昂贵的计算。
import { useMemo, useState } from 'react';
function ExpensiveCalculation({ numbers }) {
// 记忆化计算结果:只有当numbers变化时,才会重新计算
const sum = useMemo(() => {
console.log('执行计算');
return numbers.reduce((acc, num) => acc + num, 0);
}, [numbers]); // 依赖数组
return <div>总和:{sum}</div>;
}
function App() {
const [count, setCount] = useState(0);
const [numbers] = useState([1, 2, 3, 4, 5]);
return (
<div>
<ExpensiveCalculation numbers={numbers} />
<button onClick={() => setCount(count + 1)}>计数:{count}</button>
</div>
);
}
3.4 useRef:获取DOM元素与存储可变值
`useRef` 主要有两个用途:获取DOM元素引用和存储不需要引起重渲染的可变值。
import { useRef, useState, useEffect } from 'react';
function TextInputWithFocusButton() {
// 1. 获取DOM元素引用
const inputEl = useRef(null);
// 2. 存储可变值(不会引起重渲染)
const renderCount = useRef(0);
const [text, setText] = useState('');
useEffect(() => {
renderCount.current += 1; // 修改ref的值不会触发重渲染
});
const focusInput = () => {
// 通过current属性访问DOM元素
inputEl.current.focus();
};
return (
<div>
<input
ref={inputEl}
value={text}
onChange={(e) => setText(e.target.value)}
/>
<button onClick={focusInput}>聚焦输入框</button>
<p>渲染次数:{renderCount.current}</p>
</div>
);
}
四、其他实用 Hooks
| Hook | 用途说明 |
|---|---|
| `useImperativeHandle` | 自定义暴露给父组件的实例值,配合 `forwardRef` 使用 |
| `useLayoutEffect` | 与 `useEffect` 类似,但会在DOM更新后同步执行,可能阻塞浏览器绘制 |
| `useDebugValue` | 在React DevTools中为自定义Hook添加标签,便于调试 |
五、Hooks 使用规则
为了确保 React 能够正确识别和管理 Hooks,必须遵守以下规则:
- 只能在函数组件或自定义 Hook 的顶层调用 Hooks
- 不能在条件语句、循环、嵌套函数中调用 Hooks
- 错误示例:
function MyComponent() {
if (condition) {
const [count, setCount] = useState(0); // ❌ 不能在条件中调用
}
// ...
}
```

被折叠的 条评论
为什么被折叠?



