react学习之函数式组件-React 状态(State)

我爱海鲸 2026-05-02 15:14:09 暂无标签

简介vite

App.jsx:

import { useEffect, useState } from 'react'
import './App.css'

/** 函数式组件:用函数声明,内部可用 Hooks(本段演示状态驱动视图) */
function StateDrivesView({ count, setCount }) {
  const doubled = count * 2
  return (
    <section className="card">
      <h2>1. 状态驱动视图</h2>
      <p className="hint">
        下面 <code>count</code> 来自父级的 <code>useState</code>。<code>doubled</code> 只在{' '}
        <code>render</code> 中计算;<code>count</code> 一变,本函数组件会再执行一次,界面同步更新。
      </p>
      <p className="mono">
        count = <strong>{count}</strong> → 双倍(派生)=<strong>{doubled}</strong>
      </p>
      <div className="actions">
        <button type="button" onClick={() => setCount((c) => c - 1)}>
          setCount:−1
        </button>
        <button type="button" onClick={() => setCount((c) => c + 1)}>
          setCount:+1
        </button>
      </div>
    </section>
  )
}

/** 函数式组件:数组 state 的不可变更新 */
function ImmutableListDemo({ tags, setTags }) {
  return (
    <section className="card">
      <h2>2. 数组:不可变更新</h2>
      <p className="hint">
        错误做法:<code>items.push(n); setItems(items)</code>(改的是同一块引用,容易丢更新或埋雷)。正确:基于旧数组生成<strong>新数组</strong>
        ,例如 <code>[...prev, n]</code>。
      </p>
      <ul className="list">
        {tags.map((t) => (
          <li key={t.id}>{t.text}</li>
        ))}
      </ul>
      <div className="actions">
        <button
          type="button"
          onClick={() =>
            setTags((prev) => [
              ...prev,
              { id: `t-${Date.now()}`, text: `项-${prev.length + 1}` },
            ])
          }
        >
          追加一项(新数组)
        </button>
        <button type="button" onClick={() => setTags((prev) => prev.slice(0, -1))}>
          去掉最后一项(新数组)
        </button>
      </div>
    </section>
  )
}

/** 函数式组件:对象 state 的不可变更新 */
function ImmutableObjectDemo({ profile, setProfile }) {
  return (
    <section className="card">
      <h2>3. 对象:不可变更新</h2>
      <p className="hint">
        错误做法:<code>profile.level += 1; setProfile(profile)</code>。正确:浅拷贝再改,例如{' '}
        <code>{`setProfile((p) => ({ ...p, level: p.level + 1 }))`}</code>。
      </p>
      <p className="mono">
        {profile.name} · level <strong>{profile.level}</strong>
      </p>
      <div className="actions">
        <button
          type="button"
          onClick={() =>
            setProfile((p) => ({
              ...p,
              level: p.level + 1,
            }))
          }
        >
          level +1(新对象)
        </button>
      </div>
    </section>
  )
}

/** 根函数式组件:在顶层用 useState / useEffect,把状态通过 props 下发给子函数组件 */
export default function App() {
  const [count, setCount] = useState(0)
  const [tags, setTags] = useState([
    { id: 't1', text: 'React' },
    { id: 't2', text: 'useState' },
  ])
  const [profile, setProfile] = useState({ name: 'Ada', level: 1 })

  useEffect(() => {
    document.title = `State 示例 · count=${count}`
  }, [count])

  return (
    <main className="app">
      <h1>函数式组件 + 状态(useState)</h1>
      <p className="hint">
        本页组件均为<strong>函数式组件</strong>(Function Component):用 <code>function</code> 或箭头函数定义,通过{' '}
        <code>useState</code>、<code>useEffect</code> 等 Hooks 管理状态与副作用;没有 class 与{' '}
        <code>this</code>。
      </p>

      <StateDrivesView count={count} setCount={setCount} />
      <ImmutableListDemo tags={tags} setTags={setTags} />
      <ImmutableObjectDemo profile={profile} setProfile={setProfile} />
    </main>
  )
}

App.css:

.app {
  max-width: 34rem;
  margin: 0 auto;
  padding: 2rem 1.25rem 2.5rem;
  font-family: system-ui, sans-serif;
  line-height: 1.55;
  color: #1a1a1a;
}

.app > h1 {
  font-size: 1.28rem;
  font-weight: 650;
  margin: 0 0 0.65rem;
}

.hint {
  margin: 0 0 0.85rem;
  font-size: 0.9rem;
  color: #555;
}

.hint code,
.card code {
  font-size: 0.85em;
  background: #f3f4f6;
  padding: 0.08em 0.35em;
  border-radius: 4px;
}

.card {
  border: 1px solid #e5e7eb;
  border-radius: 10px;
  padding: 1rem 1.15rem 1.15rem;
  margin-bottom: 1rem;
  background: #fff;
  box-shadow: 0 1px 2px rgb(0 0 0 / 4%);
}

.card h2 {
  font-size: 1.05rem;
  font-weight: 600;
  margin: 0 0 0.5rem;
}

.card > .hint:first-of-type {
  margin-top: 0;
}

.mono {
  margin: 0 0 0.75rem;
  font-family: ui-monospace, 'Cascadia Code', monospace;
  font-size: 0.92rem;
}

.list {
  margin: 0 0 0.75rem;
  padding-left: 1.2rem;
}

.list li {
  margin-bottom: 0.25rem;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.65rem;
}

.actions button {
  padding: 0.45rem 0.95rem;
  font-size: 0.92rem;
  cursor: pointer;
  border: 1px solid #ccc;
  border-radius: 6px;
  background: #fff;
}

.actions button:hover {
  background: #f9fafb;
}

相关知识点总结:

状态驱动视图:useState 定义响应式状态,状态变化 → 组件重新渲染,界面自动更新

派生状态:直接在渲染函数中通过现有状态计算(如 doubled = count * 2),无需额外状态

状态不可变原则:React 状态不能直接修改,必须返回新值 / 新引用

数组不可变更新:使用展开运算符 [...prev]、slice 等生成新数组,禁止 push/pop 直接修改

对象不可变更新:使用展开运算符 {...prev} 浅拷贝生成新对象,禁止直接修改属性

函数式更新:setState(prev => 新值) 可安全获取上一次状态,适合依赖旧值的场景

组件通信:顶层组件管理状态,通过 props 将状态和修改方法下发给子组件

副作用:useEffect 监听状态变化,执行标题修改等操作

你好:我的2025