React Hooks 详解

1. useState - 状态管理

基本用法

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>
  )
}

高级用法

scss 复制代码
// 函数式更新
const [count, setCount] = useState(0)
setCount(prevCount => prevCount + 1)

// 对象状态
const [user, setUser] = useState({ name: '', email: '' })
setUser(prev => ({ ...prev, name: 'John' }))

2. useEffect - 副作用处理

基本用法

scss 复制代码
import { useEffect } from 'react'

function DataComponent() {
  const [data, setData] = useState(null)
  
  // 组件挂载后执行
  useEffect(() => {
    fetchData().then(setData)
  }, [])
  
  return <div>{data ? data.title : 'Loading...'}</div>
}

不同依赖数组的用法

scss 复制代码
// 1. 空依赖数组 - 只在挂载时执行一次
useEffect(() => {
  console.log('组件挂载')
}, [])

// 2. 有依赖 - 依赖变化时执行
useEffect(() => {
  console.log('count 变化了:', count)
}, [count])

// 3. 无依赖数组 - 每次渲染都执行
useEffect(() => {
  console.log('每次渲染都执行')
})

3. useContext - 上下文

基本用法

javascript 复制代码
import { createContext, useContext } from 'react'

// 1. 创建 Context
const ThemeContext = createContext()

// 2. 提供 Context
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Header />
    </ThemeContext.Provider>
  )
}

// 3. 使用 Context
function Header() {
  const theme = useContext(ThemeContext)
  return <div className={`header ${theme}`}>Header</div>
}

实际应用场景

javascript 复制代码
// 用户信息 Context
const UserContext = createContext()

function App() {
  const [user, setUser] = useState(null)
  
  return (
    <UserContext.Provider value={{ user, setUser }}>
      <Profile />
      <Settings />
    </UserContext.Provider>
  )
}

function Profile() {
  const { user } = useContext(UserContext)
  return <div>Welcome, {user?.name}</div>
}

4. useReducer - 复杂状态管理

基本用法jsx

javascript 复制代码
import { useReducer } from 'react'

// 1. 定义 reducer 函数
function counterReducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 }
    case 'decrement':
      return { count: state.count - 1 }
    case 'reset':
      return { count: 0 }
    default:
      return state
  }
}

// 2. 使用 useReducer
function Counter() {
  const [state, dispatch] = useReducer(counterReducer, { count: 0 })
  
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
    </div>
  )
}

实际应用场景jsx

javascript 复制代码
// 表单状态管理
function formReducer(state, action) {
  switch (action.type) {
    case 'SET_FIELD':
      return { ...state, [action.field]: action.value }
    case 'SET_ERRORS':
      return { ...state, errors: action.errors }
    case 'RESET':
      return { name: '', email: '', errors: {} }
    default:
      return state
  }
}

function ContactForm() {
  const [state, dispatch] = useReducer(formReducer, {
    name: '',
    email: '',
    errors: {}
  })
  
  const handleChange = (field, value) => {
    dispatch({ type: 'SET_FIELD', field, value })
  }
  
  return (
    <form>
      <input 
        value={state.name}
        onChange={(e) => handleChange('name', e.target.value)}
      />
      <input 
        value={state.email}
        onChange={(e) => handleChange('email', e.target.value)}
      />
    </form>
  )
}

5. useCallback - 函数记忆化

基本用法jsx

javascript 复制代码
import { useCallback, useState } from 'react'

function Parent() {
  const [count, setCount] = useState(0)
  const [name, setName] = useState('')
  
  // 记忆化函数,只有 count 变化时才重新创建
  const handleClick = useCallback(() => {
    console.log('Count:', count)
  }, [count])
  
  return (
    <div>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <Child onClick={handleClick} />
    </div>
  )
}

function Child({ onClick }) {
  console.log('Child 重新渲染')
  return <button onClick={onClick}>Click me</button>
}

实际应用场景jsx

javascript 复制代码
// 在您项目中可能的使用
function NewsList({ news, onNewsClick }) {
  const handleNewsClick = useCallback((newsId) => {
    onNewsClick(newsId)
  }, [onNewsClick])
  
  return (
    <div>
      {news.map(item => (
        <NewsItem 
          key={item.id} 
          news={item} 
          onClick={handleNewsClick}
        />
      ))}
    </div>
  )
}

6. useMemo - 值记忆化

基本用法jsx

javascript 复制代码
import { useMemo, useState } from 'react'

function ExpensiveComponent({ items }) {
  const [filter, setFilter] = useState('')
  
  // 只有 items 或 filter 变化时才重新计算
  const filteredItems = useMemo(() => {
    return items.filter(item => 
      item.name.toLowerCase().includes(filter.toLowerCase())
    )
  }, [items, filter])
  
  return (
    <div>
      <input 
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
        placeholder="搜索..."
      />
      <ul>
        {filteredItems.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  )
}

实际应用场景jsx

javascript 复制代码
// 在您项目中可能的使用
function NewsPlatformCard({ platform, news }) {
  // 只有 news 变化时才重新计算热门新闻
  const hotNews = useMemo(() => {
    return news.filter(item => item.isHot).slice(0, 5)
  }, [news])
  
  return (
    <div>
      <h3>{platform}</h3>
      {hotNews.map(item => (
        <div key={item.id}>{item.title}</div>
      ))}
    </div>
  )
}

7. useRef - 引用

基本用法jsx

javascript 复制代码
import { useRef, useEffect } from 'react'

function TextInput() {
  const inputRef = useRef(null)
  
  useEffect(() => {
    // 组件挂载后自动聚焦
    inputRef.current.focus()
  }, [])
  
  const handleClick = () => {
    inputRef.current.focus()
  }
  
  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>聚焦输入框</button>
    </div>
  )
}

实际应用场景jsx

javascript 复制代码
// 滚动到顶部
function ScrollToTop() {
  const topRef = useRef(null)
  
  const scrollToTop = () => {
    topRef.current.scrollIntoView({ behavior: 'smooth' })
  }
  
  return (
    <div>
      <div ref={topRef}></div>
      <button onClick={scrollToTop}>回到顶部</button>
      {/* 长内容 */}
    </div>
  )
}

8. useImperativeHandle - 命令式句柄

基本用法jsx

javascript 复制代码
import { forwardRef, useImperativeHandle, useRef } from 'react'

// 子组件
const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef(null)
  
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus()
    },
    clear: () => {
      inputRef.current.value = ''
    }
  }))
  
  return <input ref={inputRef} {...props} />
})

// 父组件
function Parent() {
  const inputRef = useRef(null)
  
  const handleClick = () => {
    inputRef.current.focus()
    inputRef.current.clear()
  }
  
  return (
    <div>
      <FancyInput ref={inputRef} />
      <button onClick={handleClick}>聚焦并清空</button>
    </div>
  )
}

9. useLayoutEffect - 同步副作用

基本用法jsx

scss 复制代码
import { useLayoutEffect, useState } from 'react'

function MeasureComponent() {
  const [width, setWidth] = useState(0)
  const divRef = useRef(null)
  
  // 在 DOM 更新后同步执行
  useLayoutEffect(() => {
    if (divRef.current) {
      setWidth(divRef.current.offsetWidth)
    }
  }, [])
  
  return (
    <div ref={divRef}>
      Width: {width}px
    </div>
  )
}

与 useEffect 的区别jsx

scss 复制代码
// useEffect - 异步执行,在浏览器绘制后
useEffect(() => {
  console.log('useEffect - 异步')
}, [])

// useLayoutEffect - 同步执行,在浏览器绘制前
useLayoutEffect(() => {
  console.log('useLayoutEffect - 同步')
}, [])

10. useDebugValue - 调试值

基本用法jsx

javascript 复制代码
import { useDebugValue, useState } from 'react'

// 自定义 Hook
function useCounter(initialValue) {
  const [count, setCount] = useState(initialValue)
  
  // 在 React DevTools 中显示调试信息
  useDebugValue(count > 10 ? 'High' : 'Low')
  
  return [count, setCount]
}

function Counter() {
  const [count, setCount] = useCounter(0)
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  )
}
相关推荐
南囝coding2 小时前
Claude Code 从入门到精通:最全配置指南和工具推荐
前端·后端
索马里亚纳海参炒贩2 小时前
useCallback useMemo memo 三个区别和作用
前端·react native
非ban必选2 小时前
netty-scoket.io路径配置
java·服务器·前端
じòぴé南冸じょうげん3 小时前
小程序的project.private.config.json是无依赖文件,那可以删除吗?
前端·小程序·json
会豪3 小时前
Electron主进程渲染进程如何优雅的进行通信
前端
jianghaha20113 小时前
前端 Word 模板参入特定数据 并且下载
前端·word
跟橙姐学代码3 小时前
轻松搞定 Python 模块与包导入:新手也能秒懂的入门指南
前端·python·ipython
aiwery3 小时前
大模型场景下的推送技术选型:轮询 vs WebSocket vs SSE
前端·agent
会豪3 小时前
前端插件-不固定高度的DIV如何增加transition
前端