Menu

React Props 教程:向组件传递数据

Props 是 React 组件的输入:父组件以 JSX 属性的形式传给子组件的值。学习如何传递和解构 props、设置默认值、传递函数、展开 props,以及为什么 props 是只读的。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

Props 是 React 组件的输入。父组件在 JSX 中以属性的形式传入它们,比如 <UserCard name="Ada" online={true} />,子组件把它们作为一个对象接收,通常会在函数参数里直接解构。有了 props,同一个组件每次使用时都能显示不同的数据。

同一个 UserCard 渲染出三个不同的人。注意最后一个:没有值的属性 online 等同于 online={true}。用你自己的名字和职位再加第四张卡片试试。

传递和读取 props

字符串用引号传入,其他值都用花括号:数字、布尔值、数组、对象、函数,甚至 JSX。

<Product
    title="Desk lamp"
    price={39.5}
    inStock={false}
    tags={['light', 'office']}
    seller={{ name: 'Lumen', rating: 4.8 }}
/>

React 会把所有属性收集到一个对象中,作为组件的第一个参数传入。你可以把它当作 props 来读取,也可以直接在参数列表里取出需要的字段:

function Product(props) {
    return <h3>{props.title}</h3>;
}

function Product({ title, price, seller }) {
    return <h3>{title}, ${price} from {seller.name}</h3>;
}

两种写法接收的是同一个对象。解构是更常见的写法,因为这样函数签名就准确列出了组件用到哪些 props。

默认值

用 JavaScript 的解构默认值 = value 给 prop 设置默认值。当父组件没有传这个 prop,或者传的是 undefined 时,默认值生效。对于 null、0、false 或 '',默认值不会生效,因为它们都是真实的值。

前三个按钮在 prop 缺失或为 undefined 的地方都用上了默认值。最后一个传入的是 null,所以 size 真的是 null,查表时找不到对应的 padding。把 size={null} 改成 size="small" 就能修好。

旧代码用静态属性设置默认值:Button.defaultProps = { size: 'medium' }。React 19 移除了函数组件的 defaultProps:在函数组件上,JSX 会直接忽略它,也不会给出警告。它在类组件上仍然有效。请使用解构默认值。

把函数作为 props 传递

数据是向下流动的:父组件把 props 传给子组件,反过来则不行。当子组件需要告诉父组件发生了什么时,父组件就传入一个函数,由子组件调用。按照惯例,这类 props 命名为 onSomething。

Rating 自己不存储任何东西。它显示收到的 value,并通过 onRate 报告点击。state 放在 App 中,再由 App 传回给它。把 handleRate 换成直接传 onRate={setRating},也一样能工作:setter 本身也是函数。像这样把 state 移到共同父组件中,叫做状态提升。

展开 props

如果你已经有一个对象,它的键和组件的 props 一一对应,就可以用 {...object} 把它展开,而不必逐个写属性。在参数列表中,剩余语法的作用正好相反:按名字取出几个 props,把其余的都收集起来再转发下去。

第二个头像先展开 person,再设置 color:同一个 prop 出现两次时,后面的那个生效,所以要把覆盖的值写在展开之后。TextField 自己留下 label,把 type、placeholder 和 maxLength 直接传给 <input>。展开适合用在这种包装组件上;在其他地方,显式列出 props 更容易看出组件拿到了什么。

props 是只读的

组件绝不能修改自己的 props。它们属于父组件,而且同一个对象可能被共享。把它们当作不允许重新赋值的函数参数:

function Counter({ count }) {
    count = count + 1; // wrong: changes a local copy, the parent never sees it
    return <p>{count}</p>;
}

function Profile(props) {
    props.name = 'Someone else'; // wrong: in development React freezes props, so this throws a TypeError
    return <p>{props.name}</p>;
}

不过 props 仍然会随时间变化。当父组件用新的值重新渲染时,子组件会收到一个新的 props 对象,并据此渲染:

Temperature 从不修改 celsius。它在每次渲染时根据 celsius 计算出 fahrenheit。拖动滑块,父组件的 state 就会变化,于是子组件拿到新的 prop。如果子组件需要一个它能自己修改的值,那个值就是 state,而不是 prop。

props 与 state

props 和 state 都是会影响组件渲染结果的普通 JavaScript 值,其中任何一个变化都会让组件重新渲染。区别在于这个值归谁所有。

PropsState
来源父组件,以 JSX 属性的形式传入组件自身,来自 useState 或 useReducer
组件能否修改不能,它是只读的能,通过它的 setter
典型用途配置组件:标签、尺寸、要显示的数据记住会变化的东西:输入的文字、展开的菜单

同一个值常常两者都是。在上面的温度例子中,celsius 在 App 中是 state,在 Temperature 中是 prop。拥有者把它作为 state 保存,再作为 prop 传下去;当子组件需要修改它时,还会一并传一个修改函数。

prop 可以存放任何值,包括 JSX 和组件。传入 icon={<StarIcon />} 这样的元素,父组件就能填充子组件中的一个插槽,思路和 children 一样,只是名字由你决定。

children prop

放在组件开始标签和结束标签之间的任何内容,都会作为名为 children 的 prop 传入。卡片、面板、布局这类事先不知道内容的包装组件,就是这样构建的:

children 一页介绍了如何传递多个插槽,以及如何安全地处理 children。

key 不是 prop

key 看起来像属性,但 React 把它留给自己,用来在多次渲染之间匹配列表项。组件永远收不到它:

控制台显示 props.key 为 undefined,而 props.id 是真实的值。当子组件需要这个标识时,就像这里的 id 一样,用另一个名字再传一次。开发构建在你读取 props.key 时还会发出警告;预览以生产构建的方式运行,所以你只会看到 undefined。

常见问题

React 中的 props 是什么?

Props(properties 的简写)是组件的参数。父组件以 JSX 属性的形式传入它们,比如 <Avatar size={48} />,子组件把它们作为一个对象接收,通常会解构:function Avatar({ size })。

如何给 prop 设置默认值?

使用解构默认值:function Button({ size = 'medium' })。当 prop 缺失或为 undefined 时,默认值生效。React 19 移除了函数组件的 defaultProps,类组件仍然支持它。

子组件能修改自己的 props 吗?

不能。props 是只读的。要改变子组件显示的内容,由父组件传入新的 props;要让子组件请求修改,父组件就传一个 onChange 之类的函数,由子组件调用。

props 和 state 有什么区别?

props 来自父组件,子组件无法修改。state 属于组件自己,通过它的 setter 修改。同一个值常常在父组件中是 state,在子组件中是 prop。

为什么 props.key 是 undefined?

key(和旧版 React 中的 ref 一样)由 React 自己用来追踪列表项,不会传给组件。如果子组件需要这个值,就用另一个名字再传一次,比如 <Row key={id} id={id} />。

Coddy 编程语言插图

和 Coddy 一起学编程

开始