React Hooks 的优势和使用场景
2025年5月18日 07:37
React Hooks 的优势和使用场景
核心优势
-
简化组件逻辑
- 告别 class 组件的繁琐生命周期
- 将相关逻辑聚合到单个 Hook 中
- 消除 this 绑定问题
// 类组件
class Example extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ count: this.state.count + 1 });
}
render() {
return <button onClick={this.handleClick}>点击 {this.state.count} 次</button>;
}
}
// 函数组件 + Hooks
function Example() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>点击 {count} 次</button>;
}
-
逻辑复用
- 自定义 Hook 实现跨组件逻辑复用
- 替代高阶组件和 render props 模式
// 自定义 Hook
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 MyComponent() {
const { width } = useWindowSize();
return <div>窗口宽度: {width}px</div>;
}
-
性能优化
- 细粒度的状态更新控制
- 避免不必要的渲染
function ExpensiveComponent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// 只有 count 变化时才重新计算
const computedValue = useMemo(() => {
return expensiveCalculation(count);
}, [count]);
// 只有在组件挂载时执行
useEffect(() => {
fetchInitialData();
}, []);
return (
<div>
<p>{computedValue}</p>
<input value={text} onChange={e => setText(e.target.value)} />
<button onClick={() => setCount(count + 1)}>增加</button>
</div>
);
}
主要使用场景
-
状态管理
-
useState
: 基础状态管理 -
useReducer
: 复杂状态逻辑
-
function TodoApp() {
const [todos, dispatch] = useReducer(todoReducer, []);
function handleAddTodo(text) {
dispatch({ type: 'ADD_TODO', text });
}
// ...
}
-
副作用处理
-
useEffect
: 数据获取、订阅、手动 DOM 操作 -
useLayoutEffect
: DOM 变更后同步执行
-
function DataFetcher({ id }) {
const [data, setData] = useState(null);
useEffect(() => {
let ignore = false;
async function fetchData() {
const result = await fetch(`/api/data/${id}`);
if (!ignore) {
setData(await result.json());
}
}
fetchData();
return () => { ignore = true; };
}, [id]); // id 变化时重新获取
// ...
}
-
性能优化
-
useMemo
: 缓存计算结果 -
useCallback
: 缓存函数引用
-
function Parent() {
const [count, setCount] = useState(0);
// 避免子组件不必要的重渲染
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []);
return (
<>
<Child onClick={handleClick} />
<div>计数: {count}</div>
</>
);
}
-
访问 DOM 元素
-
useRef
: 获取 DOM 引用或保存可变值
-
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>聚焦输入框</button>
</>
);
}
-
上下文访问
-
useContext
: 简化上下文使用
-
const ThemeContext = React.createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button style={{ background: theme === 'dark' ? '#333' : '#eee' }}>按钮</button>;
}
最佳实践
-
Hook 使用规则
- 只在 React 函数组件顶层调用 Hook
- 不要在循环、条件或嵌套函数中调用 Hook
- 自定义 Hook 必须以 "use" 开头
-
组织代码
- 将复杂逻辑拆分为多个小 Hook
- 相关逻辑组织在一起
-
性能考量
- 合理使用依赖数组
- 避免不必要的 effect 执行
- 大型列表考虑虚拟化
-
测试策略
- 使用
@testing-library/react-hooks
测试自定义 Hook - 模拟依赖项进行隔离测试
- 使用
常见问题解决方案
-
无限循环
// 错误示例 useEffect(() => { setCount(count + 1); // 会导致无限循环 }, [count]); // 正确方式 useEffect(() => { setCount(c => c + 1); // 使用函数式更新 }, []); // 空依赖数组
-
过时闭包
function Counter() { const [count, setCount] = useState(0); useEffect(() => { const id = setInterval(() => { setCount(count + 1); // 总是使用初始值 }, 1000); return () => clearInterval(id); }, []); // 缺少 count 依赖 // 正确方式 useEffect(() => { const id = setInterval(() => { setCount(c => c + 1); // 使用函数式更新 }, 1000); return () => clearInterval(id); }, []); }
-
条件执行
// 错误示例 if (condition) { useEffect(() => { ... }); // 违反 Hook 规则 } // 正确方式 useEffect(() => { if (condition) { // 在 effect 内部进行条件判断 } }, [condition]);
React Hooks 通过简化组件逻辑、提高代码复用性和优化性能,已经成为现代 React 开发的标准方式。合理运用各种 Hook 可以显著提升开发效率和代码可维护性。