React 属性入门:将静态组件转化为模板
了解 React props 如何在父组件与子组件之间传递数据,props 对象的运作原理,可以传递哪些类型的值,以及为什么单向数据流有助于提升效率。
来源: Part 5A — React Props Explained: Pass Data Between Components Like a Pro
将 React 接口拆分为组件仅是实现复用的一半。始终渲染相同文本的组件只是复制粘贴用的单元,而非构建模块。Props 是让同一个组件在每次被使用时都能渲染不同内容的机制。本指南将解释什么是 Props、数据如何从父组件传递到子组件、如何在组件内部读取 Props,以及可以传递哪些类型的值,从而帮助你将硬编码的组件转化为可复用的模板。
当组件只能显示一个内容时
一旦掌握了构建组件的方法,典型的应用结构就会像这样,由根节点 App 渲染多个部分:
App
│
├── Navbar
├── Hero
├── Features
├── Footer
现在想象一下在线商店的产品卡片。它显示产品名称、价格、评分以及购买按钮:
-------------------------
📱 iPhone 16
₹79,999
⭐⭐⭐⭐⭐
Buy Now
-------------------------
随后出现了新的要求:需要展示500种产品。按照现有的设计,每个卡片只能显示某一款手机及其对应价格,因此笔记本电脑、耳机和手表等产品都需要几乎完全相同的结构,这反而违背了使用组件的意义。我们需要在保持结构不变的同时每次提供不同的数据,而这就是props的作用。
什么是props
“Props”是“properties”的缩写,指的是父组件在渲染子组件时传递给它的值。组件负责定义结构和样式,而props则提供使每个实例具有独特性的详细信息。
用厨房做类比会更容易理解:披萨店不会为每笔订单都创造新的食谱,它有一个固定的食谱,只是会根据不同订单的要求添加不同的配料,比如额外的奶酪、蘑菇、橄榄或玉米。这里的食谱就是组件,而配料则对应props。
换句话说,组件充当模板,而属性则用于填充空白部分。无需创建始终显示相同内容的独立卡片,如下所示:
Product Card
iPhone
只需构建一个通用的产品卡片,在使用它的地方传入不同的数据即可。
父组件、子组件与单向数据流
属性的意义仅存在于组件之间的关系之中。每个 React 应用都是一棵树,其中某些组件负责渲染其他组件:
App
│
├── Navbar
├── Hero
├── ProductCard
└── Footer
这里 App 是父组件,而 Navbar、Hero、ProductCard 和 Footer 则是其子组件。父组件决定要渲染哪些子组件,同时还可以向每个子组件传递信息,这些信息就是属性。
React 要求遵循单向数据流:属性从父组件向下传递给子组件。
App
│
│ title
│ price
│ rating
▼
ProductCard
子组件会接收到title、price和rating这些属性,但它无法通过这些属性将新值传回父组件。从子组件的角度来看,这些属性也是只读的。组件绝不应修改其接收到的属性;如果某些内容需要随时间变化,那么应该将其存储在状态中,通常由父组件负责管理。当子组件需要向上层报告某些信息(如点击事件)时,父组件会通过属性传递一个函数给子组件,由子组件来调用该函数。由于数据只能单向流动,因此可以通过沿着组件树向上追溯来定位某个值的来源,这大大便于查找错误。
你的第一个带有属性的组件
首先创建一个能够根据姓名问候用户的组件。该组件接受一个名为props的参数,并在其JSX代码中读取props.name:
function Welcome(props) {
return (
<h1>Hello, {props.name}</h1>
);
}
export default Welcome;
父组件负责渲染它,并将名称作为属性传入:
import Welcome from "./Welcome";
function App() {
return (
<>
<Welcome name="Ayush" />
</>
);
}
export default App;
渲染后的结果为:
Hello, Ayush
语法各部分的含义
传递属性就如同在组件的标签上编写HTML属性一样:
<Welcome name="Ayush" />
可以将其理解为带有参数的函数调用。在接收端,组件本身就是一个函数,其第一个参数就是这些属性:
function Welcome(props)
React会将你在标签上写的所有属性汇总成一个普通的对象,然后将该对象作为参数传入。读取值则是常规的属性访问操作:
props.name
因为父组件传入了这个属性:
name="Ayush"
组件就会进行渲染:
Hello, Ayush
props 这个名称仅是一种约定。许多代码库会在参数列表中将其拆分,例如 function Welcome({ name })。
一次性传递多个属性
实际组件通常需要多个值。你可以添加任意数量的属性。注意,字符串需要用引号括起来,而数字和其他 JavaScript 值则需放在大括号内。下面的代码示例中,组件名称与第一个属性之间缺少空格;正确写法应为 <Welcome name=...:
<Welcomename="Ayush"
company="OpenAI"
experience={6}
/>
该组件会按名称逐个读取每个属性:
function Welcome(props) {
return (
<>
<h2>{props.name}</h2>
<p>{props.company}</p>
<p>{props.experience} Years</p>
</>
);
}
然后进行渲染:
Ayush
OpenAI
6 Years
更改属性后,同一个组件会显示完全不同的信息。
可复用的产品卡片
回到商店页面。现在,卡片会从属性中读取标题、价格和评分,而非直接硬编码这些信息:
function ProductCard(props) {
return (
<div>
<h2>{props.title}</h2>
<p>₹ {props.price}</p>
<p>{props.rating}</p>
</div>
);
}
export default ProductCard;
父组件可以根据需要渲染任意数量的卡片,每个卡片都有独立的数据。价格以大括号中的数字形式传递,而标题和评分则是字符串:
<ProductCard
title="iPhone 16"
price={79999}
rating="⭐⭐⭐⭐⭐"
/>
<ProductCard
title="Samsung S25"
price={68999}
rating="⭐⭐⭐⭐"
/>
<ProductCard
title="MacBook Air"
price={99999}
rating="⭐⭐⭐⭐⭐"
/>
一个组件即可展示三种不同的产品,且无需重复的标记代码。即便有500种产品,也不需要手动编写500个标签:数据来自数组,可通过products.map(...)来渲染卡片,将每个项目的字段作为属性传递,并为每张卡片分配唯一的key。
为何属性能让组件复用成为可能
如果没有属性,商店就需要为每种产品创建一个独立的组件:
ProductCard1
ProductCard2
ProductCard3
ProductCard4
ProductCard5
这意味着会有许多代码几乎完全相同的文件,每次设计变更都必须在所有地方重复操作。而使用属性的话,流程就是为同一个组件提供不同的数据:
ProductCard
↓
Different Product Data
↓
Multiple Cards
一个文件定义卡片的结构,不同实例仅数据有所差异。这正是大型 React 应用程序和设计系统背后的基本模式。
想象一下标准的申请表:每个人填写的字段都是相同的,比如姓名、年龄和地址,只是具体内容不同。空白的表格就是组件,而填写的内容则是属性。
可以传递哪些类型的值
属性并不限于文本,任何 JavaScript 值都可以传递。字符串可以使用引号:
name="Ayush"
数字需要放在大括号中,这样 React 才能接收到真正的数字,而不是字符串“26”:
age={26}
布尔值也同样需要使用大括号:
isAdmin={true}
可以传递变量的值:
name={username}
或者任何表达式的结果,该结果会在子组件接收到之前被计算:
price={500 + 200}
数组、对象和函数也可以以相同方式在大括号内传递。作为属性的函数是子组件向父组件通知事件的方式,而一个名为 children 的特殊属性则允许组件包裹其开标签和闭标签之间的所有标记内容。
硬编码值与属性
比较一下硬编码的标题:
<h2>iPhone 16</h2>
与由属性驱动的标题:
<h2>{props.title}</h2>
第一个组件只能显示一个产品,而第二个组件可以显示任意数量的产品。每当你发现自己要往某个用于重复使用的组件中输入特定内容时,先思考一下这些内容是否应该以属性的形式传递。同时也要避免另一个极端:将每一个细微之处都变成属性,这会让组件更难使用,因此只需暴露那些在不同使用场景中确实会发生变化的内容。
如需回顾组件本身的结构,可参阅React组件入门。
关键要点
- 属性(Props)是父组件在渲染子组件时传递给它的值。
- 数据流动方向是从父组件到子组件,且子组件将属性视为只读数据。
props对象中,该对象作为函数的第一个参数传递。children属性。