useState:状态管理的基石

useState 是最基础的 Hook,用于在函数组件中声明状态。它返回一个包含当前值和更新函数的数组。React 通过调用顺序来识别每个 Hook,因此必须保证 Hook 在组件的每次渲染中以相同的顺序执行。

import { useState } from 'react'

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

  // 函数式更新:基于前一个状态计算新值
  const increment = () => setCount(prev => prev + 1)

  // 惰性初始化:初始值只在首次渲染时计算
  const [data, setData] = useState(() => {
    const saved = localStorage.getItem('data')
    return saved ? JSON.parse(saved) : []
  })

  return (
    <div>
      <p>计数:{count}</p>
      <button onClick={increment}>+1</button>
    </div>
  )
}

useEffect:副作用与生命周期

useEffect 用于处理副作用,如数据请求、DOM 操作、事件订阅等。它接受一个回调函数和一个依赖数组。当依赖数组中的值发生变化时,回调函数会在渲染完成后执行。返回的清理函数会在组件卸载或下次 effect 执行前调用。

import { useEffect, useState } from 'react'

function UserProfile({ userId }) {
  const [user, setUser] = useState(null)

  useEffect(() => {
    let cancelled = false

    async function fetchUser() {
      const res = await fetch(`/api/users/${userId}`)
      const data = await res.json()
      if (!cancelled) {
        setUser(data)
      }
    }

    fetchUser()

    // 清理函数:防止组件卸载后的状态更新和竞态条件
    return () => {
      cancelled = true
    }
  }, [userId]) // 仅在 userId 变化时重新执行

  if (!user) return <p>加载中...</p>
  return <h2>{user.name}</h2>
}

useRef:跨越渲染的持久引用

useRef 返回一个可变的 ref 对象,其 .current 属性可以在组件的整个生命周期内保持不变。修改 .current 不会触发重新渲染,这使得它非常适合存储 DOM 引用、定时器 ID 或任何不需要驱动视图更新的可变值。

import { useRef, useEffect } from 'react'

function TextInput() {
  const inputRef = useRef(null)
  const renderCount = useRef(0)

  useEffect(() => {
    inputRef.current?.focus()
  }, [])

  // 不推荐用 ref 追踪渲染次数来做副作用,此处仅作演示
  renderCount.current++

  return <input ref={inputRef} placeholder="自动聚焦" />
}

useCallback 与 useMemo:性能优化

useCallback 缓存函数引用,useMemo 缓存计算结果。它们都接受一个工厂函数和依赖数组,仅在依赖变化时重新计算。这两个 Hook 的核心价值在于避免不必要的子组件重渲染或重复计算。

import { useCallback, useMemo, useState } from 'react'

function SearchPanel({ items }) {
  const [query, setQuery] = useState('')

  // useMemo:缓存过滤结果
  const filteredItems = useMemo(() => {
    return items.filter(item =>
      item.name.toLowerCase().includes(query.toLowerCase())
    )
  }, [items, query])

  // useCallback:缓存事件处理函数
  const handleChange = useCallback((e) => {
    setQuery(e.target.value)
  }, [])

  return (
    <div>
      <input onChange={handleChange} placeholder="搜索..." />
      <ul>
        {filteredItems.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  )
}

useContext:跨层级数据传递

useContext 让组件直接订阅 Context 的值,避免逐层传递 props(即 prop drilling)。当 Context 值变化时,所有消费该 Context 的组件都会重新渲染。

import { createContext, useContext, useState } from 'react'

const ThemeContext = createContext('light')

function App() {
  const [theme, setTheme] = useState('light')
  return (
    <ThemeContext.Provider value={theme}>
      <Toolbar />
      <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
        切换主题
      </button>
    </ThemeContext.Provider>
  )
}

function Toolbar() {
  return <ThemeButton />
}

function ThemeButton() {
  const theme = useContext(ThemeContext)
  return <button className={theme}>当前主题:{theme}</button>
}

自定义 Hook:逻辑复用的最佳方式

自定义 Hook 是以 use 开头的函数,内部可以调用其他 Hook。它允许将组件逻辑提取到可复用的函数中,是 React 推荐的逻辑复用模式。

function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    try {
      const item = localStorage.getItem(key)
      return item ? JSON.parse(item) : initialValue
    } catch {
      return initialValue
    }
  })

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value))
  }, [key, value])

  return [value, setValue]
}

// 使用
function Settings() {
  const [theme, setTheme] = useLocalStorage('theme', 'light')
  return (
    <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
      主题:{theme}
    </button>
  )
}

Hooks 的规则与常见陷阱

React Hooks 有两条铁律:只在函数组件或自定义 Hook 的顶层调用 Hook,不要在循环、条件或嵌套函数中调用。这是因为 React 依赖调用顺序来匹配 Hook 和其对应的状态。违反这条规则会导致状态错乱。

常见的陷阱包括:闭包陷阱(effect 中引用了过期的变量)、无限循环(useEffect 缺少依赖或依赖引用每次都变化)、遗漏依赖(eslint-plugin-react-hooks 可以帮助检测)。使用函数式更新可以规避许多闭包问题,而在依赖数组中正确声明所有引用则是避免 bug 的关键。