程序员面试宝典

一站式面试准备平台

返回分类
React中级

React Hooks 深入解析

深入理解 React Hooks 的工作原理、使用场景和最佳实践

2026-03-16
阅读时间: 11分钟

React Hooks 深入解析

什么是 Hooks?

Hooks 是 React 16.8 引入的新特性,让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

为什么需要 Hooks?

  1. 逻辑复用困难:在 class 组件中,逻辑复用通常需要高阶组件或 render props
  2. 复杂组件难以理解:生命周期方法中经常包含不相关的逻辑
  3. class 的困惑:需要理解 JavaScript 中 this 的工作方式

基础 Hooks

useState

javascript
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useEffect

javascript
import { useState, useEffect } from 'react';

function DataFetcher() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    // 组件挂载时执行
    fetch('/api/data')
      .then(response => response.json())
      .then(data => {
        setData(data);
        setLoading(false);
      });
    
    // 清理函数(可选)
    return () => {
      // 组件卸载时执行清理
    };
  }, []); // 依赖数组,空数组表示只执行一次
  
  if (loading) return <div>Loading...</div>;
  
  return <div>{JSON.stringify(data)}</div>;
}

常用 Hooks

useContext

javascript
import { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  const theme = useContext(ThemeContext);
  return <div>Current theme: {theme}</div>;
}

useReducer

javascript
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:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });
  
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
    </>
  );
}

自定义 Hooks

自定义 Hook 是一个函数,其名称以 "use" 开头,函数内部可以调用其他的 Hook。

useLocalStorage

javascript
import { useState, useEffect } from 'react';

function useLocalStorage(key, initialValue) {
  // 从 localStorage 读取初始值
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });
  
  // 更新 localStorage
  useEffect(() => {
    try {
      window.localStorage.setItem(key, JSON.stringify(storedValue));
    } catch (error) {
      console.error(error);
    }
  }, [key, storedValue]);
  
  return [storedValue, setStoredValue];
}

// 使用示例
function App() {
  const [name, setName] = useLocalStorage('name', 'John');
  
  return (
    <input
      value={name}
      onChange={e => setName(e.target.value)}
    />
  );
}

Hooks 规则

  1. 只在最顶层使用 Hook:不要在循环、条件或嵌套函数中调用 Hook
  2. 只在 React 函数中调用 Hook:在 React 函数组件或自定义 Hook 中调用

常见面试题

1. useState 和 useReducer 的区别

特性useStateuseReducer
适用场景简单的状态逻辑复杂的状态逻辑
状态更新直接设置新值通过 dispatch action
性能优化自动合并更新需要手动优化
测试相对简单更容易测试

2. useEffect 的依赖数组

javascript
useEffect(() => {
  // 1. 没有依赖数组:每次渲染都执行
});

useEffect(() => {
  // 2. 空依赖数组:只执行一次(componentDidMount)
}, []);

useEffect(() => {
  // 3. 有依赖数组:依赖变化时执行
}, [count, name]);

3. 实现 usePrevious Hook

javascript
import { useRef, useEffect } from 'react';

function usePrevious(value) {
  const ref = useRef();
  
  useEffect(() => {
    ref.current = value;
  }, [value]);
  
  return ref.current;
}

// 使用示例
function Counter() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);
  
  return (
    <div>
      <p>Now: {count}, before: {prevCount}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}

最佳实践

  1. 按逻辑关注点组织 Hooks:将相关的逻辑放在同一个 useEffect 中
  2. 提取自定义 Hooks:当逻辑复用时,提取为自定义 Hook
  3. 使用 useCallback 和 useMemo 优化性能:避免不必要的重新渲染
  4. 保持 Hook 的纯净:不要在 Hook 中执行副作用(除了 useEffect)
  5. 使用 eslint-plugin-react-hooks:自动检查 Hook 规则

性能优化

useMemo

javascript
import { useMemo } from 'react';

function ExpensiveComponent({ a, b }) {
  const result = useMemo(() => {
    // 昂贵的计算
    return computeExpensiveValue(a, b);
  }, [a, b]); // 只有 a 或 b 变化时重新计算
  
  return <div>{result}</div>;
}

useCallback

javascript
import { useCallback } from 'react';

function ParentComponent() {
  const [count, setCount] = useState(0);
  
  const handleClick = useCallback(() => {
    console.log('Clicked!', count);
  }, [count]); // 只有 count 变化时重新创建函数
  
  return <ChildComponent onClick={handleClick} />;
}

常见陷阱

  1. 无限循环:在 useEffect 中更新依赖的状态
  2. 过时闭包:在异步回调中访问过时的状态
  3. 条件渲染中的 Hook:违反 Hook 规则
  4. 忘记清理:在 useEffect 中创建了需要清理的资源

相关标签