好久没写技术文章了,最近翻看了自己刚学 React 时做的笔记,决定整理一下我常用的八个 React Hooks(如果算上自定义 Hooks 应该是九个,但今天先聚焦这八个)。我刚开发时是个 React 小白,看了一天公司项目后发现必须补习 Hooks,于是周末恶补了一波,做了些总结。这篇文章将结合代码示例和实际场景,带你逐一掌握这些 Hooks。虽然内容有点长,但你可以挑自己感兴趣的 Hooks 重点阅读。准备好了吗?让我们一起解锁 React Hooks 的魅力吧!
1. useState:状态管理的起点
类组件 vs 函数组件
在 React 中,状态管理是核心。类组件用 this.state 定义状态,而函数组件则靠 useState。来看看两者的对比:
类组件实现
import React from 'react';
class StateClass extends React.Component {
constructor() {
super();
this.state = { name: '类' };
}
setName = () => {
this.setState({ name: '我通过类组件方法变成这样了' });
};
render() {
return (
<div onClick={this.setName}>
这是一个类组件 —— {this.state.name}
</div>
);
}
}
export default StateClass;
函数组件实现
import React, { useState } from 'react';
function StateFunction() {
const [name, setName] = useState('函数');
return (
<div onClick={() => setName('我使用 Hooks 变成这样了')}>
这是一个函数组件 —— {name}
</div>
);
}
export default StateFunction;
使用要点:
- useState 返回一个数组:[状态值, 更新函数]。
- 初始值可以是任意类型(字符串、对象等)。
- 更新函数支持直接赋值或基于前值的回调(如 setName(val => val + 'xxx'))。
场景:需要管理简单状态时,比如表单输入、计数器等。相比类组件,useState 写法更简洁,直观易懂。
2. useEffect:副作用的魔法师
什么是副作用?
副作用是指渲染之外的操作,比如数据请求、事件绑定、手动改 DOM 等。useEffect 就是为函数组件添加“生命周期”功能的利器,执行时机在渲染结束后。
无依赖:每次渲染都执行
import React, { useState, useEffect } from 'react';
function Counter() {
const [num, setNum] = useState(0);
useEffect(() => {
console.log('渲染结束啦!');
});
return (
<div onClick={() => setNum(num + 1)}>
点击我 —— {num}
</div>
);
}
空依赖:只执行一次
useEffect(() => {
console.log('只在首次渲染时执行');
}, []);
指定依赖:依赖变化时执行
useEffect(() => {
console.log('num 变了,重新执行');
}, [num]);
清除副作用:避免内存泄漏
绑定事件时,必须清理,否则会重复绑定导致性能问题:
useEffect(() => {
const updateMouse = (e) => {
console.log(`鼠标位置:${e.clientX}, ${e.clientY}`);
};
document.addEventListener('click', updateMouse);
return () => {
document.removeEventListener('click', updateMouse);
console.log('清理完成');
};
}, []);
执行顺序:
- 首次渲染:执行 useEffect 的回调。
- 更新时:先执行上次的清理函数(return),再执行新的回调。
- 卸载时:执行最后一次清理。
场景:数据请求、事件监听、定时器等。空依赖适合初始化加载,带依赖适合动态更新。
3. useLayoutEffect:DOM 操作的精准助手
与 useEffect 的区别
useLayoutEffect 和 useEffect 用法相同,但执行时机不同:
示例对比
-
useEffect:渲染结束后执行(异步)。
-
useLayoutEffect:DOM 更新后、渲染前执行(同步)。
-
import React, { useState, useEffect, useLayoutEffect } from 'react'; function Demo() { const [num, setNum] = useState(0); useLayoutEffect(() => { console.log('useLayoutEffect 先执行'); }, [num]); useEffect(() => { console.log('useEffect 后执行'); }, [num]); return ( <div onClick={() => setNum(num + 1)}> 点击我 —— {num} </div> ); }
场景:需要同步操作 DOM(比如测量元素尺寸、调整布局)时用 useLayoutEffect,其他情况优先 useEffect 以避免阻塞渲染。
4. useMemo:性能优化的利器
作用
useMemo 缓存计算结果,只有依赖变化时才重新计算,避免不必要的重复执行。
优化复杂计算
-
import React, { useState, useMemo } from 'react'; function Calculator() { const [num, setNum] = useState(1); const [age, setAge] = useState(18); const doubleNum = useMemo(() => { console.log(`计算双倍:${num}`); return num * 2; // 假设复杂计算 }, [num]); return ( <div onClick={() => setAge(age + 1)}> 双倍值:{doubleNum} <br /> 年龄:{age} </div> ); }
未优化:每次渲染都重新计算 doubleNum,即使 num 不变。
-
优化后:只有 num 变化时才重新计算。
优化子组件渲染
-
import React, { useState, useMemo } from 'react'; import { memo } from 'react'; const Child = memo(({ info }) => { console.log('子组件渲染'); return <p>姓名:{info.name}</p>; }); function Parent() { const [show, setShow] = useState(true); const info = useMemo(() => ({ name: 'Even', age: 22 }), []); return ( <div> <Child info={info} /> <button onClick={() => setShow(!show)}>切换</button> </div> ); }
场景:复杂计算、防止子组件因父组件无关更新而重复渲染。
5. useCallback:函数缓存的专家
与 useMemo 的区别
-
useMemo:缓存返回值(可以是任意类型)。
-
useCallback:缓存函数本身。
-
import React, { useState, useMemo, useCallback } from 'react'; function Demo() { const [num, setNum] = useState(1); const [age, setAge] = useState(18); const memoizedValue = useMemo(() => num * 2, [num]); // 返回值 const memoizedFn = useCallback(() => num * 2, [num]); // 返回函数 return ( <div onClick={() => setAge(age + 1)}> Memo 值:{memoizedValue} <br /> Callback 值:{memoizedFn()} <br /> 年龄:{age} </div> ); }
优化子组件
-
import React, { useState, useCallback } from 'react'; function Child({ callback }) { useEffect(() => { console.log('子组件更新'); }, [callback]); return <p>结果:{callback}</p>; } function Parent() { const [num, setNum] = useState(1); const getDoubleNum = useCallback(() => num * 2, [num]); return ( <div onClick={() => setNum(num + 1)}> <Child callback={getDoubleNum()} /> 父组件:{num} </div> ); }
场景:传递函数给子组件时,避免因函数引用变化导致子组件重复渲染。
6. useRef:持久化数据的守护者
作用
useRef 返回一个在组件生命周期内保持不变的引用对象,常用于保存数据或 DOM 引用。
示例:清除定时器
-
import React, { useState, useEffect, useRef } from 'react'; function Timer() { const [num, setNum] = useState(0); const timerRef = useRef(); useEffect(() => { timerRef.current = setInterval(() => setNum(n => n + 1), 400); }, []); useEffect(() => { if (num > 10) { clearInterval(timerRef.current); console.log('定时器已清除'); } }, [num]); return <div>计数:{num}</div>; }
未使用 useRef:定时器 ID 丢失,无法清除。 使用 useRef:通过 ref.current 保存 ID,精准清除。
特点:修改 ref.current 不会触发重新渲染。
场景:保存定时器、访问 DOM 元素(如聚焦输入框)。
7. useContext:状态共享的桥梁
作用
useContext 让子组件轻松共享父组件的状态,避免逐层传递 props。
未优化
-
function Parent() { const [num, setNum] = useState(1); return ( <div> <button onClick={() => setNum(num + 1)}>加 1</button> <Child1 num={num} /> <Child2 num={num} /> </div> ); }
使用 useContext
-
import React, { useState, useContext, createContext } from 'react'; const NumContext = createContext(null); function Parent() { const [num, setNum] = useState(1); return ( <NumContext.Provider value={num}> <button onClick={() => setNum(num + 1)}>加 1</button> <Child1 /> <Child2 /> </NumContext.Provider> ); } function Child1() { const num = useContext(NumContext); return <p>子组件 1:{num}</p>; } function Child2() { const num = useContext(NumContext); return <p>子组件 2:{num + 2}</p>; }
8. useReducer:复杂状态的掌舵手
作用
useReducer 类似 Redux,适合管理复杂状态逻辑。
示例
-
import React, { useReducer } from 'react'; const initialState = { age: 18, num: 1 }; const reducer = (state, action) => { switch (action.type) { case 'add': return { ...state, num: state.num + 1 }; default: return state; } }; function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <button onClick={() => dispatch({ type: 'add' })}>加 1</button> 计数:{state.num} <br /> 年龄:{state.age} </div> ); }
-
总结:Hooks 让 React 更简单
这八个 Hooks 是 React 函数组件的基石:
-
useState:基础状态管理。
-
useEffect / useLayoutEffect:处理副作用。
-
useMemo / useCallback:性能优化。
-
useRef:持久化数据。
-
useContext:状态共享。
-
useReducer:复杂状态管理。
-
从类组件的繁琐到 Hooks 的简洁,React 的开发体验变得更优雅。希望这篇文章能帮你快速上手这些 Hooks,并在项目中灵活运用。如果你有疑问或想聊聊自定义 Hooks,欢迎留言交流!
关键词:React Hooks 教程、useState 示例、useEffect 副作用、useMemo 优化、React 函数组件开发。