Skip to content

Props:让组件动起来

在上一部分,我们学习了 JSX 的基本语法,并用它创建了一个静态的 PracticeCard 组件骨架。这个卡片目前只能显示固定的内容。如果我们想用这个卡片组件展示不同的练习标题和描述,就需要一种方法将这些变化的数据传递给它。

这就是 Props (属性) 发挥作用的地方。Props 是 React 中实现组件可配置性可复用性的关键机制。它允许我们将数据从“父”组件流向“子”组件。

学习目标:

  • 理解 Props 的核心概念:父组件向子组件传递数据。
  • 掌握在父组件中通过 JSX 属性语法传递 Props。
  • 学会在子组件中接收并使用 Props 数据。
  • (可选) 了解如何使用解构赋值简化 Props 的接收。
  • 能够渲染多个具有不同 Props 的组件实例。

Props 概念

Props(是 Properties 的缩写)是 React 组件用来相互通信的一种方式,特别是用于从父组件向子组件传递数据。你可以把 React 组件想象成 JavaScript 函数,而 Props 就好比是这些函数的参数。

当父组件渲染一个子组件时,它可以像给 HTML 标签添加属性一样,给子组件标签添加一些自定义的属性,这些属性和它们的值就会被收集到一个名为 props 的对象中,并传递给子组件。

核心特点:

  1. 数据流向: 数据通过 Props 单向从父组件流向子组件。
  2. 只读性: 子组件应该将 Props 视为只读的。它不能直接修改接收到的 Props。如果需要修改,应该使用 State(后续课程内容)。
  3. 实现复用: 同一个组件可以通过接收不同的 Props 来展示不同的内容或行为,从而实现复用。

传递 Props (在父组件中)

在父组件(例如 app/page.js)的 JSX 中渲染子组件(例如 <PracticeCard />)时,你可以像添加 HTML 属性一样,将数据作为 Props 传递给子组件。

这完全利用了我们之前学习的 JSX 语法:

  • 属性名: 你自定义的 Prop 名称 (e.g., title, description)。
  • 属性值:
    • 如果是字符串字面量,使用引号 ""
    • 如果是JavaScript 表达式(变量、数字、布尔值、对象、数组、函数调用等),使用花括号 {}

示例 (app/page.js):

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):

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 使用解构):

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 数据流的关键。