Props:让组件动起来
在上一部分,我们学习了 JSX 的基本语法,并用它创建了一个静态的 PracticeCard
组件骨架。这个卡片目前只能显示固定的内容。如果我们想用这个卡片组件展示不同的练习标题和描述,就需要一种方法将这些变化的数据传递给它。
这就是 Props (属性) 发挥作用的地方。Props 是 React 中实现组件可配置性和可复用性的关键机制。它允许我们将数据从“父”组件流向“子”组件。
学习目标:
- 理解 Props 的核心概念:父组件向子组件传递数据。
- 掌握在父组件中通过 JSX 属性语法传递 Props。
- 学会在子组件中接收并使用 Props 数据。
- (可选) 了解如何使用解构赋值简化 Props 的接收。
- 能够渲染多个具有不同 Props 的组件实例。
Props 概念
Props(是 Properties 的缩写)是 React 组件用来相互通信的一种方式,特别是用于从父组件向子组件传递数据。你可以把 React 组件想象成 JavaScript 函数,而 Props 就好比是这些函数的参数。
当父组件渲染一个子组件时,它可以像给 HTML 标签添加属性一样,给子组件标签添加一些自定义的属性,这些属性和它们的值就会被收集到一个名为 props
的对象中,并传递给子组件。
核心特点:
- 数据流向: 数据通过 Props 单向从父组件流向子组件。
- 只读性: 子组件应该将 Props 视为只读的。它不能直接修改接收到的 Props。如果需要修改,应该使用 State(后续课程内容)。
- 实现复用: 同一个组件可以通过接收不同的 Props 来展示不同的内容或行为,从而实现复用。
传递 Props (在父组件中)
在父组件(例如 app/page.js
)的 JSX 中渲染子组件(例如 <PracticeCard />
)时,你可以像添加 HTML 属性一样,将数据作为 Props 传递给子组件。
这完全利用了我们之前学习的 JSX 语法:
- 属性名: 你自定义的 Prop 名称 (e.g.,
title
,description
)。 - 属性值:
- 如果是字符串字面量,使用引号
""
。 - 如果是JavaScript 表达式(变量、数字、布尔值、对象、数组、函数调用等),使用花括号
{}
。
- 如果是字符串字面量,使用引号
示例 (app/page.js
):
import PracticeCard from '@/components/PracticeCard'; // 确保路径正确
export default function HomePage() { const weekOneTitle = "第一周:组件基础"; const practiceDate = new Date().toLocaleDateString();
return ( <div> <h1>每周练习</h1>
{/* 使用类似 HTML 属性的 JSX 语法传递 Props */} <PracticeCard title={weekOneTitle} {/* 使用 {} 传递变量 */} description="学习 React 组件的核心概念和 JSX 基础。" {/* 使用 "" 传递字符串 */} date={practiceDate} {/* 使用 {} 传递变量 */} completed={false} {/* 使用 {} 传递布尔值 */} />
{/* 可以传递其他任何需要的数据 */} <PracticeCard title="第二周:Props 与 State" description="掌握组件间数据传递和内部状态管理。" date={practiceDate} completed={true} /> </div> );}
接收和使用 Props (在子组件中)
子组件(这里是 PracticeCard.js
)通过其函数参数来接收父组件传递过来的所有 Props。这个参数通常被命名为 props
,它是一个对象,包含了所有传递过来的属性键值对。
示例 (components/PracticeCard.js
):
// 函数的第一个参数 `props` 是一个包含所有传入属性的对象export default function PracticeCard(props) { // props 对象看起来会像这样 (对于第一个卡片实例): // { // title: "第一周:组件基础", // description: "学习 React 组件的核心概念和 JSX 基础。", // date: "...", // 当前日期字符串 // completed: false // }
console.log(props); // 可以在控制台查看接收到的 props
return ( // 使用 props 对象中的数据来动态渲染内容 // 同样,这里使用 JSX 的 {} 来嵌入 props 中的 JavaScript 值 <div className="practice-card" style={{ border: `2px solid ${props.completed ? 'green' : '#eee'}`, padding: '16px', margin: '10px 0', borderRadius: '8px' }}> <h2>{props.title}</h2> <p>{props.description}</p> <p>日期: {props.date}</p> <p>状态: {props.completed ? '已完成' : '未完成'}</p> </div> );}
在子组件的 JSX 中,我们再次使用花括号 {}
来嵌入 props
对象中的值(例如 {props.title}
),从而将动态数据渲染到界面上。我们甚至可以在 JSX 中直接使用 Props 进行简单的逻辑判断(如根据 props.completed
改变边框颜色或显示文本)。
解构 Props (可选,但推荐)
每次都写 props.
有点繁琐。为了让代码更简洁易读,通常推荐在函数参数位置直接使用 JavaScript 的解构赋值 (Destructuring Assignment) 来提取需要的 Props。
示例 (components/PracticeCard.js
使用解构):
// 直接在参数位置解构 props 对象,提取需要的属性export default function PracticeCard({ title, description, date, completed }) { // 现在可以直接使用变量名 title, description, date, completed // 无需 props. 前缀
return ( <div className="practice-card" style={{ border: `2px solid ${completed ? 'green' : '#eee'}`, padding: '16px', margin: '10px 0', borderRadius: '8px' }}> <h2>{title}</h2> <p>{description}</p> <p>日期: {date}</p> <p>状态: {completed ? '已完成' : '未完成'}</p> </div> );}
这种方式更清晰地表明了该组件期望接收哪些 Props,并且在组件内部使用时更加方便。
渲染多个卡片实例
Props 的核心价值在于复用。现在我们的 PracticeCard
组件是动态的了,我们可以在父组件 (page.js
) 中根据需要渲染任意多个实例,只需为每个实例传递不同的 Props 数据即可。
回到 app/page.js
的例子,我们已经展示了如何渲染两个具有不同 title
, description
, completed
值的 PracticeCard
实例。这比复制粘贴整个卡片组件的代码要高效得多,也更易于维护。
// app/page.js (片段)// ... <PracticeCard title={weekOneTitle} description="学习 React 组件的核心概念和 JSX 基础。" date={practiceDate} completed={false} />
<PracticeCard title="第二周:Props 与 State" description="掌握组件间数据传递和内部状态管理。" date={practiceDate} completed={true} />
{/* 可以继续添加更多... */} <PracticeCard title="第三周:事件处理" description="让你的组件响应用户交互。" date={practiceDate} completed={false} />// ...
总结
Props 是 React 中实现组件动态化和复用的基础。通过结合 JSX 的属性语法,我们可以轻松地将数据从父组件传递给子组件。子组件接收 Props(推荐使用解构赋值),并使用这些数据来渲染不同的 UI。记住,Props 是单向流动且只读的,这是理解 React 数据流的关键。