在最近的面试中,我发现面试官对 React Hooks 的问题几乎是必问的。这足以说明 Hooks 在 React 开发中的重要性。如果你对 Hooks 还不够熟悉,那么这篇文章将帮助你深入理解并"化解"面试官的攻击。
一、Hooks 是什么?
React Hooks 是 React 16.8 引入的一项核心特性,它允许函数组件使用 state、生命周期、上下文等原本只能在 class 组件中使用的功能。Hooks 的引入不仅简化了组件逻辑,还提高了代码的可复用性和可测试性。
二、为什么需要 Hooks?
在 Hooks 出现之前,函数组件被称为"无状态组件",只能接收 props 并返回 UI,无法使用 state 或生命周期方法。而 class 组件虽然功能强大,但存在以下问题:
- 逻辑复用困难:高阶组件(HOC)和 render props 虽然能复用逻辑,但会导致"嵌套地狱"。
- 复杂组件难以理解:一个组件中可能包含多个不相关的生命周期逻辑,导致代码难以维护。
- 类组件学习成本高 :需要理解
this
绑定、生命周期顺序等概念。
Hooks 的出现解决了这些问题,它让函数组件也能拥有完整的 React 能力。
三、内置 Hooks 详解
1. useState:状态管理
用于在函数组件中添加局部状态。
javascript
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
点击次数:{count}
</button>
);
}
useState
返回一个数组,第一个是当前状态值,第二个是更新状态的函数。- 初始值只会在组件首次渲染时使用。
2. useEffect:处理副作用
用于在函数组件中执行副作用操作,如数据获取、订阅或手动更改 DOM。
javascript
import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/user/${userId}`)
.then(res => res.json())
.then(setUser);
}, [userId]); // 依赖数组,userId 变化时重新执行
return user ? <div>{user.name}</div> : <p>加载中...</p>;
}
- 依赖数组为空时,effect 只在组件挂载和卸载时执行一次。
- 返回一个清理函数,用于清除副作用(如取消订阅、定时器)。
3. useContext:共享全局状态
用于跨组件共享数据,避免 props 层层传递。
javascript
const ThemeContext = React.createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button style={{ background: theme }}>按钮</button>;
}
- 需要配合
React.createContext
使用。
4. useReducer:复杂状态管理
当状态逻辑复杂时,可以使用 useReducer
替代 useState
。
javascript
const initialState = { count: 0 };
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, initialState);
return (
<>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</>
);
}
- 类似 Redux 的用法,适合复杂状态逻辑。
5. useMemo 和 useCallback:性能优化
useMemo
缓存计算结果,避免重复计算。useCallback
缓存函数,避免子组件不必要的重新渲染。
javascript
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => doSomething(a, b), [a, b]);
面试官问了我一个问题:useMemo
如何使用会降低性能?我回答:如果将它用于简单的计算,比如简单的加减法,这就是不必要的优化,反而会降低性能。
四、自定义 Hooks:逻辑复用
自定义 Hooks 是 React 的逻辑复用机制,命名必须以 use
开头。
javascript
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}</div>;
}
五、Hooks 规则
使用 Hooks 时必须遵守以下两条规则:
- 只在最顶层调用 Hooks:不能在循环、条件或嵌套函数中调用。
- 只在 React 函数组件或自定义 Hooks 中调用:不能在普通 JavaScript 函数中调用。
六、Hooks 最佳实践
- 拆分复杂逻辑:将复杂逻辑拆分为多个自定义 Hooks。
- 合理使用依赖数组:避免遗漏依赖导致 bug。
- 避免过度优化 :不要过早使用
useMemo
或useCallback
,除非有性能问题。 - 使用 ESLint 插件 :安装
eslint-plugin-react-hooks
自动检查 Hooks 使用规则。
七、总结
React Hooks 彻底改变了 React 的编程模式,使函数组件成为主流。它不仅简化了组件逻辑,还提高了代码的可读性和可维护性。掌握 Hooks 是现代 React 开发者的必备技能。在面试中,如果你能熟练运用这些知识,一定会给面试官留下深刻印象。
希望这篇文章的格式和内容能够满足你的需求。如果你有任何进一步的修改建议或需要补充的内容,请随时告诉我。