CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
React 核心/课时/Props 与 State
RawBlame
课时目录14 节

React 核心

  1. 01React 是什么
  2. 02JSX 与渲染
  3. 03函数组件
  4. 04Props 与 State
  5. 05事件处理
  6. 06条件渲染与列表
  7. 07useEffect
  8. 08useRef 与 DOM
  9. 09useMemo、useCallback 与自定义 Hook
  10. 10表单与受控组件
  11. 11Context 共享状态
  12. 12React Router 入门
  13. 13数据获取与错误边界
  14. 14综合练习与 Next.js 方向

Props 与 State

受控数据流、useState 基础。

35 分钟

Props 与 State

这一课你将能做到

  1. 用 props 把数据从父传到子
  2. 用 useState 记住会变的值,并触发界面更新
  3. 说出何时该「状态提升」到父组件

为什么要学

本站路径卡的标题从哪来?父页面把课名传进去——这是 props。
「离开页」输入框里你正在打的字——这是 state。

  • Props:父给子的说明书(只读)
  • State:组件自己的记性(会变,一变就重渲染)

分不清就会:子组件硬改 props、或两个组件各存一份「是否完成」,勾了左边右边还没勾。人话:外来参数 vs 自己记得住的东西。

先建立图像

父组件  --props-->  子组件(只读展示)
父组件  <--事件通知--  子组件(「我被点了」)
组件内部 state:点击后 setState → React 再调用函数 → 新 JSX
PropsState
谁给的父(或默认值)组件自己 useState
能不能直接改不要改用 setter
变了会怎样父重渲染,子收到新 props本组件重渲染

跟着做

步骤 1:props(解构写法最常见)

function StationTitle({ title }) {
  return <h2>{title}</h2>;
}

export function Demo() {
  return <StationTitle title="HTML 入门" />;
}

子组件里不要写 title = "别的" 去改 props。要改,让父改传进来的值。
多个属性就是多个参数:<Card title="JS" done={false} />。

步骤 2:useState(最小记忆)

import { useState } from "react";

export function Counter() {
  const [count, setCount] = useState(0);
  // count:这次渲染看到的值
  // setCount:更新函数;调用后安排一次重新渲染

  return (
    <button type="button" onClick={() => setCount(count + 1)}>
      点了 {count} 次
    </button>
  );
}

人话:useState(0) 交给 React 一个盒子,盒子里先是 0。你只能通过 setCount 换盒子里的东西;换了 React 才愿意再画一遍。

步骤 3:更新依赖旧值时用函数式

setCount((c) => c + 1);

连续点、或在同一个事件里加两次时更稳妥。直接 setCount(count + 1) 两次可能仍只用到同一份旧 count。

步骤 4:对象/数组 state 要「换新的」

const [item, setItem] = useState({ done: false, title: "CSS" });

// 错误:item.done = true; setItem(item);  // 还是同一个对象
// 正确:做出新对象
setItem({ ...item, done: true });

const [list, setList] = useState([1, 2]);
setList([...list, 3]);          // 追加
setList(list.filter((x) => x !== 2)); // 删除

React 靠「是不是新引用」判断要不要更新。直接改原对象/原数组,引用没变,界面可能不刷新。

步骤 5:状态提升(两个孩子抢同一份真相)

场景:列表项和页脚都要显示「完成了几课」。
若孩子各自 useState,数字会对不上。
把 doneCount 放在共同的父组件,再 props 传下去;孩子通过 onToggle 通知父去 setState。

function Child({ done, onToggle }) {
  return (
    <button type="button" onClick={onToggle}>
      {done ? "已完成" : "未完成"}
    </button>
  );
}

export function Parent() {
  const [done, setDone] = useState(false);
  return (
    <>
      <Child done={done} onToggle={() => setDone((d) => !d)} />
      <p>{done ? "父也知道完成了" : "父知道未完成"}</p>
    </>
  );
}

步骤 6:state 更新要用新值

setCount(c => c + 1); // 连续点击更稳
// setCount(count + 1); setCount(count + 1); // 可能只 +1

讲清楚要点

  1. props 向下,事件向上(常见模式)。子不改共享数据,只喊「发生了点击」。
  2. 能算出来的别存 state(全名 = 姓+名,只存姓和名;完成数 = 列表 filter 的 length)。
  3. useState(初始值) 的初始值只在首次挂载用到。之后父传入的新 props 不会自动重置这个 state,除非你用 key 重建组件,或自己写同步(多数时候不要同步)。
  4. 一个状态一个职责;对象很大时更新用展开,避免漏字段。
  5. Hooks 只能写在函数组件顶层:不要放进 if/for。

你可能会懵的地方

  • 为什么不能 count++ 然后不管?
    必须 setCount,否则 React 不知道要重渲染。count++ 还可能改到「这次渲染的快照」,行为诡异。

  • setState 后立刻 console.log(count) 还是旧值?
    更新是安排下一次渲染;当下这次函数里的 count 还是旧快照。要看新值,看下一次渲染,或用函数式更新里的参数。

  • 受控输入
    输入框的 value 绑 state、onChange 里 setState,就是受控组件(表单课)。

  • props 变了但 state 还是旧的?
    正常。state 是组件自己的记忆。若「父换了一门课」应重置,给组件加 key={slug} 让 React 当新实例。

  • props 变了子不更新?
    先确认是否真换了引用/值;再查是否错误 memo 或 key 稳定导致复用错位。

错法对照

错法问题正解
改 props单向数据流被破坏通知父改
突变数组再 set可能不刷新新数组 [...arr]
所有东西进全局 / Context难追更新来源能本地就本地
把派生值再存一份 state两份数据打架渲染时计算
把 props 拷进 state 再忘同步UI 过期能算就不存;必要时 key 重置

自测三问

  1. props 和 state 的只读/可变差别?
  2. 为何更新对象要用展开成新对象?
  3. 什么叫状态提升?举一个「两个组件必须看同一份勾选」的场景。

小结一句话

props 是外来参数,state 是内部记忆;改界面就 setState,并且用新数据替换旧数据。

动手加深

做一个计数器:显示 count,按钮 +1;再加「重置」。口述:谁持有 state、谁只收 props。

延伸

  • react.dev State: A Component's Memory
  • 下一课:事件处理。
加载本节练习…
← 上一课函数组件下一课 →事件处理