state 和 props 有什么区别?

一、是什么

1.1 Props

Props(Properties 的缩写)是组件的外部输入,由父组件传递给子组件。对于接收 props 的组件而言,props 是只读的——子组件不能直接修改自己的 props。

function UserCard({ name, age, avatar }) {
  return (
    <div className="card">
      <img src={avatar} alt={name} />
      <h2>{name}</h2>
      <p>年龄: {age}</p>
    </div>
  );
}

function App() {
  return (
    <UserCard name="张三" age={28} avatar="/avatar.jpg" />
  );
}

Props 可以传递任何 JavaScript 值:字符串、数字、布尔值、对象、数组、函数,甚至 JSX:

function Layout({ header, children, onNavigate }) {
  return (
    <div>
      <nav>{header}</nav>
      <main>{children}</main>
      <button onClick={onNavigate}>返回首页</button>
    </div>
  );
}

function App() {
  return (
    <Layout
      header={<h1>网站标题</h1>}
      onNavigate={() => window.location.href = "/"}
    >
      <p>这是页面内容,通过 children 传递</p>
    </Layout>
  );
}

1.2 State

State 是组件的内部状态,由组件自身管理。状态的变化会触发组件重新渲染:

function Counter() {
  const [count, setCount] = useState(0);
  const [step, setStep] = useState(1);

  return (
    <div>
      <p>当前计数: {count}</p>
      <label>
        步长:
        <input
          type="number"
          value={step}
          onChange={(e) => setStep(Number(e.target.value))}
        />
      </label>
      <button onClick={() => setCount((c) => c + step)}>增加</button>
      <button onClick={() => setCount(0)}>重置</button>
    </div>
  );
}

在类组件中,state 是一个对象,通过 this.setState 更新:

class Counter extends React.Component {
  state = { count: 0, step: 1 };

  increment = () => {
    this.setState((prev) => ({ count: prev.count + prev.step }));
  };

  render() {
    return (
      <div>
        <p>当前计数: {this.state.count}</p>
        <button onClick={this.increment}>增加</button>
      </div>
    );
  }
}

二、核心区别

2.1 所有权(Ownership)

function Parent() {
  const [theme, setTheme] = useState("light");

  // theme 是 Parent 的 state
  // 但传给 Child 后,成为 Child 的 props
  return <Child theme={theme} onToggle={() => setTheme(t => t === "light" ? "dark" : "light")} />;
}

function Child({ theme, onToggle }) {
  // theme 和 onToggle 是 props,Child 不能直接修改它们
  return (
    <div className={theme}>
      <button onClick={onToggle}>切换主题</button>
    </div>
  );
}

同一份数据,在拥有它的组件中是 state,传递给子组件后就是 props。这体现了 React 单向数据流的核心理念。

2.2 可变性(Mutability)

Props 是只读的

function BadComponent({ count }) {
  // 错误!不能修改 props
  count = count + 1;

  // 错误!不能修改 props 的属性
  // props.user.name = "新名字";

  return <p>{count}</p>;
}

State 通过特定方法更新

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

  // 正确:通过 setter 函数更新
  const increment = () => setCount((c) => c + 1);

  // 错误!直接修改不会触发重新渲染
  // count = count + 1;

  return <button onClick={increment}>{count}</button>;
}

2.3 数据流向

         ┌──────────────┐
         │   App        │  state: { user, theme }
         │              │
         └──┬───────┬───┘
      props ↓       ↓ props
    ┌───────┴──┐ ┌──┴────────┐
    │  Header  │ │  Content  │  state: { isOpen }
    │          │ │           │
    └──────────┘ └─────┬─────┘
                 props ↓
               ┌───────┴──────┐
               │   Article    │  state: { likes }
               └──────────────┘

数据从父组件流向子组件(自上而下),子组件通过回调函数通知父组件更新(自下而上的事件流)。

2.4 完整对比表

特性PropsState
来源父组件传递组件内部定义
可变性只读可通过 setState / setter 更新
所有权属于父组件属于当前组件
触发渲染父组件更新传入新值调用更新函数
数据流向父 → 子仅限当前组件(或向下传递为 props)

三、常见使用模式

3.1 状态提升(Lifting State Up)

当两个兄弟组件需要共享数据时,将状态提升到共同的父组件:

function TemperatureConverter() {
  const [celsius, setCelsius] = useState(0);

  const fahrenheit = celsius * 9 / 5 + 32;

  return (
    <div>
      <TemperatureInput
        label="摄氏度"
        value={celsius}
        onChange={setCelsius}
      />
      <TemperatureInput
        label="华氏度"
        value={fahrenheit}
        onChange={(f) => setCelsius((f - 32) * 5 / 9)}
      />
    </div>
  );
}

function TemperatureInput({ label, value, onChange }) {
  return (
    <label>
      {label}:
      <input
        type="number"
        value={value.toFixed(1)}
        onChange={(e) => onChange(Number(e.target.value))}
      />
    </label>
  );
}

3.2 Props Drilling 问题

当深层嵌套的组件需要顶层数据时,props 需要一层层传递,即使中间组件不使用这些数据:

function App() {
  const [theme, setTheme] = useState("light");
  return <Page theme={theme} />; // 传递
}

function Page({ theme }) {
  return <Section theme={theme} />; // 传递
}

function Section({ theme }) {
  return <Button theme={theme} />; // 传递
}

function Button({ theme }) {
  return <button className={theme}>点击</button>; // 最终使用
}

解决方案包括 Context、状态管理库(Zustand、Jotai)或组件组合模式:

// 方案一:使用 Context
const ThemeContext = createContext("light");

function App() {
  const [theme, setTheme] = useState("light");
  return (
    <ThemeContext.Provider value={theme}>
      <Page />
    </ThemeContext.Provider>
  );
}

function Button() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>点击</button>;
}

// 方案二:组件组合(Component Composition)
function App() {
  const [theme, setTheme] = useState("light");
  return (
    <Page>
      <Section>
        <Button theme={theme} />
      </Section>
    </Page>
  );
}

function Page({ children }) {
  return <div className="page">{children}</div>;
}

3.3 默认 Props 与类型校验

// 函数组件:使用参数默认值 + TypeScript 类型定义
interface AvatarProps {
  src: string;
  size?: number;
  alt?: string;
}

function Avatar({ src, size = 48, alt = "头像" }: AvatarProps) {
  return <img src={src} width={size} height={size} alt={alt} />;
}

四、什么时候用 State,什么时候用 Props?

判断标准:

  1. 这个数据会随时间变化吗? 如果不会,可能连 state 都不需要,用常量即可
  2. 这个数据可以从 props 或其他 state 计算得出吗? 如果可以,不要存为 state,直接计算
  3. 这个数据属于谁? 只有当前组件需要 → state;需要传递给子组件 → 在当前组件是 state,传下去是 props
function ProductList({ products }) {
  const [sortBy, setSortBy] = useState("price");
  const [filterText, setFilterText] = useState("");

  // 派生数据:不需要额外的 state
  const filteredProducts = products
    .filter((p) => p.name.includes(filterText))
    .sort((a, b) => a[sortBy] - b[sortBy]);

  return (
    <div>
      <input
        value={filterText}
        onChange={(e) => setFilterText(e.target.value)}
        placeholder="搜索..."
      />
      <select value={sortBy} onChange={(e) => setSortBy(e.target.value)}>
        <option value="price">按价格</option>
        <option value="rating">按评分</option>
      </select>
      {filteredProducts.map((p) => (
        <ProductCard key={p.id} product={p} />
      ))}
    </div>
  );
}

五、总结

Props 和 State 是 React 数据管理的两大基石。Props 实现组件间的数据传递,State 实现组件内部的状态管理。理解两者的区别和配合方式,是掌握 React 单向数据流的关键。

面试要点:能清晰阐述 props 是只读的外部输入、state 是可变的内部数据;理解状态提升和 props drilling 问题及解决方案;知道什么数据应该作为 state,什么应该通过计算派生。