Skip to content

React 框架基础

简介

React 是用于构建用户界面的 JavaScript 库,核心思想是组件化、声明式 UI、单向数据流。

环境搭建

sh
npm create vite@latest my-app -- --template react
npm create vite@latest my-app -- --template react-ts   # TypeScript

cd my-app
npm install
npm run dev

JSX

jsx
// JSX 是 JavaScript 的语法扩展,允许在 JS 中写 HTML-like 代码
const element = <h1>Hello, React!</h1>

// 嵌入表达式
const name = "React"
const element = <h1>Hello, {name}</h1>

// 属性
const img = <img src={url} alt="描述" />

// 条件渲染
{isLoggedIn && <UserGreeting />}
{isLoggedIn ? <LogoutButton /> : <LoginButton />}

// 列表渲染
{items.map(item => <li key={item.id}>{item.name}</li>)}

组件

函数组件(推荐)

jsx
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>
}

// 箭头函数
const Welcome = ({ name }) => <h1>Hello, {name}</h1>

组件组合

jsx
function App() {
  return (
    <div>
      <Welcome name="Alice" />
      <Welcome name="Bob" />
    </div>
  )
}

Props

jsx
function User({ name, age, isAdmin, children }) {
  return (
    <div>
      <h2>{name}</h2>
      <p>Age: {age}</p>
      {isAdmin && <span>管理员</span>}
      {children}
    </div>
  )
}

// 使用
<User name="Alice" age={30} isAdmin>
  <p>额外的子元素</p>
</User>

State

jsx
import { useState } from 'react'

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

  return (
    <div>
      <p>计数: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(prev => prev + 1)}>+1(函数式更新)</button>
    </div>
  )
}

useState 注意事项

jsx
// 对象 state(需展开合并)
const [user, setUser] = useState({ name: '', age: 0 })
setUser({ ...user, name: 'Alice' })

// 数组 state
const [items, setItems] = useState([1, 2, 3])
setItems([...items, 4])           // 追加
setItems(items.filter(i => i > 1)) // 删除
setItems(items.map(i => i * 2))   // 更新

useEffect

jsx
import { useState, useEffect } from 'react'

function DataFetcher() {
  const [data, setData] = useState(null)

  // 组件挂载时执行一次
  useEffect(() => {
    fetch('/api/data')
      .then(res => res.json())
      .then(setData)
  }, [])  // 空依赖:仅在挂载时执行

  // 依赖变化时执行
  useEffect(() => {
    document.title = `点击了 ${count} 次`
  }, [count])

  // 清理函数(组件卸载或依赖变化前执行)
  useEffect(() => {
    const timer = setInterval(() => setCount(c => c + 1), 1000)
    return () => clearInterval(timer)
  }, [])

  return <div>{JSON.stringify(data)}</div>
}

事件处理

jsx
function Form() {
  const [value, setValue] = useState('')

  const handleSubmit = (e) => {
    e.preventDefault()    // 阻止默认行为
    console.log(value)
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={value} onChange={e => setValue(e.target.value)} />
      <button type="submit">提交</button>
    </form>
  )
}

受控组件

jsx
function LoginForm() {
  const [form, setForm] = useState({ email: '', password: '' })

  const handleChange = (e) => {
    setForm({ ...form, [e.target.name]: e.target.value })
  }

  return (
    <form>
      <input name="email" value={form.email} onChange={handleChange} />
      <input name="password" type="password" value={form.password} onChange={handleChange} />
    </form>
  )
}

useRef

jsx
import { useRef } from 'react'

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

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

  return (
    <>
      <input ref={inputRef} />
      <button onClick={focus}>聚焦输入框</button>
    </>
  )
}

useContext

jsx
import { createContext, useContext } from 'react'

// 创建 Context
const ThemeContext = createContext('light')

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

// 消费者
function Toolbar() {
  const theme = useContext(ThemeContext)
  return <div className={theme}>当前主题: {theme}</div>
}

useReducer

jsx
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 }
    case 'reset':
      return { count: 0 }
    default:
      throw new Error('未知 action')
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 })

  return (
    <div>
      <p>计数: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+1</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
      <button onClick={() => dispatch({ type: 'reset' })}>重置</button>
    </div>
  )
}

自定义 Hook

jsx
import { useState, useEffect } from 'react'

// 自定义 Hook(必须以 use 开头)
function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth)

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth)
    window.addEventListener('resize', handleResize)
    return () => window.removeEventListener('resize', handleResize)
  }, [])

  return width
}

// 使用
function MyComponent() {
  const width = useWindowWidth()
  return <p>窗口宽度: {width}px</p>
}

常用自定义 Hook 示例

jsx
// 防抖
function useDebounce(value, delay = 300) {
  const [debouncedValue, setDebouncedValue] = useState(value)

  useEffect(() => {
    const timer = setTimeout(() => setDebouncedValue(value), delay)
    return () => clearTimeout(timer)
  }, [value, delay])

  return debouncedValue
}

// 异步请求
function useFetch(url) {
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)

  useEffect(() => {
    setLoading(true)
    fetch(url)
      .then(res => res.json())
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false))
  }, [url])

  return { data, loading, error }
}

React 路由

sh
npm install react-router-dom
jsx
import { BrowserRouter, Routes, Route, Link, useParams, useNavigate } from 'react-router-dom'

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">首页</Link>
        <Link to="/about">关于</Link>
        <Link to="/users/1">用户</Link>
      </nav>

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/users/:id" element={<UserDetail />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  )
}

function UserDetail() {
  const { id } = useParams()
  const navigate = useNavigate()
  return (
    <div>
      <p>用户 ID: {id}</p>
      <button onClick={() => navigate('/')}>返回首页</button>
    </div>
  )
}

React 与 TypeScript

tsx
interface UserProps {
  name: string
  age: number
  avatar?: string       // 可选
  onUpdate: (id: number) => void
}

const User: React.FC<UserProps> = ({ name, age, onUpdate }) => {
  return (
    <div>
      <p>{name} ({age})</p>
      <button onClick={() => onUpdate(1)}>更新</button>
    </div>
  )
}

// 泛型 Hook
function useLocalStorage<T>(key: string, initial: T) {
  const [value, setValue] = useState<T>(() => {
    const stored = localStorage.getItem(key)
    return stored ? JSON.parse(stored) : initial
  })

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

  return [value, setValue] as const
}

常见模式

条件渲染

jsx
{loading && <Spinner />}
{error ? <Error msg={error} /> : <Data />}

列表与 key

jsx
{items.map(item => <ListItem key={item.id} item={item} />)}

提升状态

jsx
// 将共享 state 提升到共同的父组件
function Parent() {
  const [value, setValue] = useState('')
  return (
    <>
      <Input value={value} onChange={setValue} />
      <Display value={value} />
    </>
  )
}

React 开发者工具

sh
# Chrome 扩展
React Developer Tools

# 浏览器中可查看:
# - 组件树与 props/state
# - 性能分析(Profiler)
# - 组件高亮

常用库

用途
react-router-dom路由
axiosHTTP 请求
react-query / @tanstack/react-query服务端状态管理
zustand轻量状态管理
redux toolkit大型应用状态管理
react-hook-form表单处理
zod / yup表单验证
antd / element-plusUI 组件库
tailwindcss原子化 CSS
date-fns / dayjs日期处理
react-i18next国际化
recharts图表

蜀ICP备2025150039号