Hooks基础 #

一、Hooks概述 #

1.1 什么是Hooks #

Hooks 是 React 16.8 引入的新特性,允许在函数组件中使用状态和其他 React 特性。

1.2 为什么使用Hooks #

优势 说明
简洁 函数组件比类组件更简洁
复用 自定义 Hook 实现逻辑复用
组合 相关逻辑放在一起
无this 不需要处理 this 绑定问题

1.3 Hooks规则 #

javascript
// 规则一:只在最顶层使用Hook
function Component() {
  const [count, setCount] = useState(0);
  
  // ❌ 不要在条件语句中使用
  if (count > 0) {
    const [name, setName] = useState(''); // 错误
  }
  
  // ❌ 不要在循环中使用
  for (let i = 0; i < 5; i++) {
    const [value, setValue] = useState(i); // 错误
  }
  
  // ✅ 在顶层使用
  const [name, setName] = useState('');
  
  return <div>{count}</div>;
}

// 规则二:只在React函数中使用Hook
// ✅ 在函数组件中使用
function Component() {
  const [state, setState] = useState(0);
}

// ✅ 在自定义Hook中使用
function useCustomHook() {
  const [state, setState] = useState(0);
}

// ❌ 不要在普通函数中使用
function regularFunction() {
  const [state, setState] = useState(0); // 错误
}

二、useState #

2.1 基本用法 #

javascript
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>增加</button>
    </div>
  );
}

2.2 初始化状态 #

javascript
// 直接值
const [count, setCount] = useState(0);
const [name, setName] = useState('React');
const [isActive, setIsActive] = useState(false);

// 对象
const [user, setUser] = useState({ name: '', age: 0 });

// 数组
const [items, setItems] = useState([]);

// 惰性初始化(计算开销大时使用)
const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation();
  return initialState;
});

2.3 更新状态 #

javascript
function Counter() {
  const [count, setCount] = useState(0);

  // 方式一:直接设置新值
  const increment = () => {
    setCount(count + 1);
  };

  // 方式二:函数式更新(推荐)
  const incrementSafe = () => {
    setCount(prev => prev + 1);
  };

  // 批量更新
  const incrementMultiple = () => {
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
    // 最终 count 增加 3
  };

  return (
    <div>
      <p>{count}</p>
      <button onClick={increment}>增加</button>
    </div>
  );
}

2.4 更新对象和数组 #

javascript
function Form() {
  const [user, setUser] = useState({
    name: '',
    email: '',
    address: {
      city: '',
      street: ''
    }
  });

  // 更新顶层属性
  const updateName = (name) => {
    setUser(prev => ({ ...prev, name }));
  };

  // 更新嵌套属性
  const updateCity = (city) => {
    setUser(prev => ({
      ...prev,
      address: {
        ...prev.address,
        city
      }
    }));
  };

  return (
    <form>
      <input
        value={user.name}
        onChange={(e) => updateName(e.target.value)}
      />
      <input
        value={user.address.city}
        onChange={(e) => updateCity(e.target.value)}
      />
    </form>
  );
}

function TodoList() {
  const [todos, setTodos] = useState([]);

  // 添加
  const addTodo = (text) => {
    setTodos(prev => [...prev, { id: Date.now(), text }]);
  };

  // 删除
  const removeTodo = (id) => {
    setTodos(prev => prev.filter(todo => todo.id !== id));
  };

  // 更新
  const updateTodo = (id, text) => {
    setTodos(prev => prev.map(todo =>
      todo.id === id ? { ...todo, text } : todo
    ));
  };

  return (
    <div>
      <button onClick={() => addTodo('New Task')}>添加</button>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            {todo.text}
            <button onClick={() => removeTodo(todo.id)}>删除</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

三、useRef #

3.1 基本用法 #

javascript
import { useRef } from 'react';

function TextInput() {
  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} />
      <button onClick={focusInput}>聚焦</button>
    </div>
  );
}

3.2 useRef特点 #

特点 说明
持久化 组件生命周期内保持不变
不触发渲染 修改 current 不会重新渲染
任意值 可以存储任何类型的值

3.3 存储可变值 #

javascript
function Stopwatch() {
  const [time, setTime] = useState(0);
  const intervalRef = useRef(null);

  const start = () => {
    if (intervalRef.current) return;
    
    intervalRef.current = setInterval(() => {
      setTime(prev => prev + 1);
    }, 1000);
  };

  const stop = () => {
    if (intervalRef.current) {
      clearInterval(intervalRef.current);
      intervalRef.current = null;
    }
  };

  const reset = () => {
    stop();
    setTime(0);
  };

  return (
    <div>
      <p>{time}秒</p>
      <button onClick={start}>开始</button>
      <button onClick={stop}>停止</button>
      <button onClick={reset}>重置</button>
    </div>
  );
}

3.4 获取前一次的值 #

javascript
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>当前: {count}</p>
      <p>之前: {prevCount}</p>
      <button onClick={() => setCount(count + 1)}>增加</button>
    </div>
  );
}

四、useMemo #

4.1 基本用法 #

javascript
import { useMemo } from 'react';

function ExpensiveComponent({ list, filter }) {
  const filteredList = useMemo(() => {
    console.log('计算中...');
    return list.filter(item => item.includes(filter));
  }, [list, filter]);

  return (
    <ul>
      {filteredList.map(item => <li key={item}>{item}</li>)}
    </ul>
  );
}

4.2 使用场景 #

javascript
function ProductList({ products, category }) {
  // 缓存计算结果
  const filteredProducts = useMemo(() => {
    return products.filter(p => p.category === category);
  }, [products, category]);

  // 缓存排序结果
  const sortedProducts = useMemo(() => {
    return [...filteredProducts].sort((a, b) => a.price - b.price);
  }, [filteredProducts]);

  // 缓存复杂计算
  const statistics = useMemo(() => {
    const total = filteredProducts.reduce((sum, p) => sum + p.price, 0);
    const avg = total / filteredProducts.length;
    return { total, avg };
  }, [filteredProducts]);

  return (
    <div>
      <p>平均价格: {statistics.avg}</p>
      {sortedProducts.map(product => (
        <div key={product.id}>{product.name}</div>
      ))}
    </div>
  );
}

五、useCallback #

5.1 基本用法 #

javascript
import { useCallback } from 'react';

function ParentComponent() {
  const [count, setCount] = useState(0);
  const [items, setItems] = useState([]);

  // 缓存回调函数
  const handleClick = useCallback(() => {
    console.log('点击');
  }, []);

  // 带依赖的回调
  const handleAddItem = useCallback(() => {
    setItems(prev => [...prev, `Item ${prev.length + 1}`]);
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>增加Count</button>
      <ChildComponent onClick={handleClick} onAdd={handleAddItem} />
    </div>
  );
}

const ChildComponent = React.memo(function ChildComponent({ onClick, onAdd }) {
  console.log('ChildComponent 渲染');
  return (
    <div>
      <button onClick={onClick}>点击</button>
      <button onClick={onAdd}>添加</button>
    </div>
  );
});

5.2 useMemo vs useCallback #

javascript
// useMemo: 缓存计算结果
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

// useCallback: 缓存函数引用
const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

// useCallback 等价于
const memoizedCallback = useMemo(() => () => {
  doSomething(a, b);
}, [a, b]);

六、useReducer #

6.1 基本用法 #

javascript
import { useReducer } from 'react';

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 };
    case 'reset':
      return initialState;
    case 'set':
      return { count: action.payload };
    default:
      throw new Error('Unknown action');
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset' })}>重置</button>
      <button onClick={() => dispatch({ type: 'set', payload: 10 })}>
        设置为10
      </button>
    </div>
  );
}

6.2 复杂状态管理 #

javascript
const initialState = {
  todos: [],
  filter: 'all'
};

function todoReducer(state, action) {
  switch (action.type) {
    case 'add':
      return {
        ...state,
        todos: [...state.todos, {
          id: Date.now(),
          text: action.payload,
          completed: false
        }]
      };
    case 'toggle':
      return {
        ...state,
        todos: state.todos.map(todo =>
          todo.id === action.payload
            ? { ...todo, completed: !todo.completed }
            : todo
        )
      };
    case 'delete':
      return {
        ...state,
        todos: state.todos.filter(todo => todo.id !== action.payload)
      };
    case 'set_filter':
      return {
        ...state,
        filter: action.payload
      };
    default:
      return state;
  }
}

function TodoApp() {
  const [state, dispatch] = useReducer(todoReducer, initialState);

  const addTodo = (text) => {
    dispatch({ type: 'add', payload: text });
  };

  return (
    <div>
      <TodoInput onAdd={addTodo} />
      <TodoList
        todos={state.todos}
        onToggle={(id) => dispatch({ type: 'toggle', payload: id })}
        onDelete={(id) => dispatch({ type: 'delete', payload: id })}
      />
    </div>
  );
}

6.3 惰性初始化 #

javascript
function init(initialCount) {
  return { count: initialCount };
}

function Counter({ initialCount }) {
  const [state, dispatch] = useReducer(reducer, initialCount, init);
  
  return (
    <div>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'reset', payload: initialCount })}>
        Reset
      </button>
    </div>
  );
}

七、useContext #

7.1 基本用法 #

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

const ThemeContext = createContext('light');

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

function Toolbar() {
  return <ThemedButton />;
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Themed Button</button>;
}

7.2 结合useState使用 #

javascript
const UserContext = createContext(null);

function App() {
  const [user, setUser] = useState(null);

  return (
    <UserContext.Provider value={{ user, setUser }}>
      <Header />
      <Main />
    </UserContext.Provider>
  );
}

function Header() {
  const { user, setUser } = useContext(UserContext);

  if (!user) {
    return <button onClick={() => setUser({ name: 'Alice' })}>登录</button>;
  }

  return (
    <div>
      <span>{user.name}</span>
      <button onClick={() => setUser(null)}>退出</button>
    </div>
  );
}

八、Hooks速查表 #

Hook 用途
useState 状态管理
useEffect 副作用处理
useContext 上下文访问
useReducer 复杂状态管理
useRef DOM引用/持久值
useMemo 缓存计算结果
useCallback 缓存函数引用
useLayoutEffect 同步DOM操作

九、总结 #

要点 说明
规则 只在顶层调用,只在React函数中调用
useState 管理组件状态
useRef 访问DOM,存储持久值
useMemo 缓存计算结果
useCallback 缓存函数引用
useReducer 复杂状态逻辑

核心原则:

  • 遵循 Hooks 调用规则
  • 使用函数式更新保证状态正确
  • 合理使用 useMemo 和 useCallback 优化性能
  • 复杂状态使用 useReducer
最后更新:2026-03-26