React Props 与数据渲染
前端框架 Props ES6一、React Props(属性)
1.1 什么是 Props
Props = Properties = 传递给组件的 自定义属性。
类比 HTML 属性:
html
<!-- HTML — 预定义属性 -->
<input id="fName" placeholder="First Name" value="Angela" />
<!-- React — 自定义属性(Props) -->
<Card name="Beyoncé" img="url..." tel="123456" email="b@mail.com" />1.2 传递 Props
jsx
// 使用组件时,像 HTML 属性一样传递数据
<Card
name="Beyoncé"
img="https://example.com/beyonce.jpg"
tel="+123 456 789"
email="b@email.com"
/>1.3 接收 Props
组件函数的 第一个参数 就是 props 对象:
javascript
function Card(props) {
// props = {
// name: "Beyoncé",
// img: "https://example.com/beyonce.jpg",
// tel: "+123 456 789",
// email: "b@email.com"
// }
return (
<div className="card">
<h2>{props.name}</h2>
<img src={props.img} alt="avatar" />
<p>{props.tel}</p>
<p>{props.email}</p>
</div>
);
}1.4 组件复用
Props 的核心价值 = 一个组件,多组数据:
jsx
<Card name="Beyoncé" img="..." tel="123" email="b@mail.com" />
<Card name="Jack" img="..." tel="456" email="j@mail.com" />
<Card name="Chuck" img="..." tel="789" email="c@mail.com" />每个 <Card /> 使用相同的组件代码,但渲染不同的数据。
1.5 Props 的关键规则
重要
HTML 属性(如 className、style、onClick)只能用在 HTML 元素 上,不能用在自定义组件上。
jsx
// ❌ 自定义组件上的 className 会被当作 prop,不会应用样式
<Card className="my-style" />
// ✅ className 必须放在 HTML 元素上
function Card(props) {
return <div className="my-style">...</div>;
}二、用数据数组渲染组件
2.1 数据与 UI 分离
最佳实践:将数据存储在单独的文件中:
javascript
// contacts.js
const contacts = [
{
id: 1,
name: "Beyoncé",
imgURL: "https://example.com/beyonce.jpg",
phone: "+123 456 789",
email: "b@email.com"
},
{
id: 2,
name: "Jack Bauer",
imgURL: "https://example.com/jack.jpg",
phone: "+987 654 321",
email: "j@email.com"
}
];
export default contacts;2.2 使用 map 渲染列表
jsx
import contacts from "./contacts";
function App() {
return (
<div>
<h1>My Contacts</h1>
{contacts.map(contact => (
<Card
key={contact.id}
name={contact.name}
img={contact.imgURL}
tel={contact.phone}
email={contact.email}
/>
))}
</div>
);
}key 属性
React 使用 key 来追踪列表中每个元素的身份。key 必须是 唯一 的(通常用 id)。
jsx
// ❌ 没有 key → 控制台警告
{items.map(item => <Card name={item.name} />)}
// ✅ 有 key → React 高效更新
{items.map(item => <Card key={item.id} name={item.name} />)}三、ES6 核心特性
3.1 箭头函数(Arrow Functions)
javascript
// 传统函数
function double(x) {
return x * 2;
}
// 箭头函数
const double = (x) => {
return x * 2;
};
// 单表达式 — 隐式返回(省略 return 和花括号)
const double = x => x * 2;在 map 中的应用:
javascript
const numbers = [3, 56, 2, 48, 5];
// 传统写法
const doubled = numbers.map(function(num) {
return num * 2;
});
// 箭头函数简写
const doubled = numbers.map(num => num * 2);
// → [6, 112, 4, 96, 10]3.2 map — 映射转换
对数组的每个元素执行操作,返回 新数组:
javascript
const numbers = [3, 56, 2, 48, 5];
const doubled = numbers.map(x => x * 2);
// → [6, 112, 4, 96, 10]
const strings = numbers.map(x => x.toString() + "!!");
// → ["3!!", "56!!", "2!!", "48!!", "5!!"]React 中的 map — 渲染组件列表:
jsx
{emojis.map(emoji => (
<Entry
key={emoji.id}
emoji={emoji.symbol}
name={emoji.name}
meaning={emoji.meaning}
/>
))}3.3 filter — 过滤筛选
返回满足条件的元素组成的 新数组:
javascript
const numbers = [3, 56, 2, 48, 5];
const big = numbers.filter(x => x > 10);
// → [56, 48]
const small = numbers.filter(x => x <= 10);
// → [3, 2, 5]3.4 reduce — 累计计算
将数组 归约 为单个值:
javascript
const numbers = [3, 56, 2, 48, 5];
const sum = numbers.reduce((accumulator, current) => {
return accumulator + current;
});
// 运算过程:
// 第1次:accumulator=3, current=56 → 59
// 第2次:accumulator=59, current=2 → 61
// 第3次:accumulator=61, current=48 → 109
// 第4次:accumulator=109,current=5 → 114
// → 1143.5 find — 查找第一个匹配
返回 第一个 满足条件的元素(非数组):
javascript
const numbers = [3, 56, 2, 48, 5];
const found = numbers.find(x => x > 10);
// → 56(第一个大于 10 的元素)3.6 findIndex — 查找索引
返回第一个满足条件的元素的 索引:
javascript
const numbers = [3, 56, 2, 48, 5];
const index = numbers.findIndex(x => x > 10);
// → 1(56 的索引位置)3.7 数组方法对比
| 方法 | 返回值 | 用途 |
|---|---|---|
map | 新数组(等长) | 转换每个元素 |
filter | 新数组(≤原长) | 筛选元素 |
reduce | 单个值 | 累计计算 |
find | 单个元素 | 找到第一个匹配 |
findIndex | 数字(索引) | 找到第一个匹配的位置 |
forEach | undefined | 遍历(无返回) |
四、条件渲染
4.1 三元运算符(Ternary Operator)
jsx
// 语法:condition ? expressionIfTrue : expressionIfFalse
{isLoggedIn ? <h1>Hello!</h1> : <Login />}实际应用:
javascript
const currentTime = new Date().getHours();
{currentTime < 12
? <h1>Good Morning</h1>
: <h1>Good Afternoon</h1>
}4.2 && 运算符(短路求值)
当只需要在条件为 true 时渲染,没有 else 分支:
jsx
// 语法:condition && expression
// condition 为 true → 渲染 expression
// condition 为 false → 什么都不渲染
{unreadMessages.length > 0 &&
<h2>You have {unreadMessages.length} unread messages.</h2>
}原理:
javascript
true && "Hello" // → "Hello"(返回右边)
false && "Hello" // → false(返回左边,React 不渲染 false)4.3 条件渲染模式对比
| 模式 | 适用场景 | 示例 |
|---|---|---|
三元 ? : | 两个分支(if/else) | {isLoggedIn ? <Home /> : <Login />} |
&& | 只有 if(无 else) | {showWarning && <Warning />} |
| 提前 return | 组件级条件 | if (!data) return <Loading />; |
五、ES6 解构赋值(Destructuring)
5.1 对象解构
javascript
const person = { name: "Angela", age: 26, hobby: "cooking" };
// 传统方式
const name = person.name;
const age = person.age;
// ES6 解构
const { name, age, hobby } = person;
// name → "Angela", age → 26, hobby → "cooking"Props 解构:
javascript
// 传统方式
function Card(props) {
return <h2>{props.name}</h2>;
}
// 解构方式 ⭐
function Card({ name, img, tel, email }) {
return (
<div>
<h2>{name}</h2>
<img src={img} alt="avatar" />
<p>{tel}</p>
<p>{email}</p>
</div>
);
}5.2 数组解构
javascript
const [r, g, b] = [255, 128, 64];
// r → 255, g → 128, b → 64
// 常见于 React Hooks
const [count, setCount] = useState(0);5.3 默认值
javascript
const { name = "Anonymous", age = 0 } = {};
// name → "Anonymous", age → 0
const [first = 1, second = 2] = [10];
// first → 10, second → 2六、ES6 展开运算符(Spread Operator)
6.1 数组展开
javascript
const citrus = ["Lime", "Lemon", "Orange"];
const fruits = ["Apple", ...citrus, "Banana"];
// → ["Apple", "Lime", "Lemon", "Orange", "Banana"]
// 合并数组
const all = [...arr1, ...arr2];6.2 对象展开
javascript
const fullName = { fName: "James", lName: "Bond" };
const user = { ...fullName, id: 1, email: "007@mi6.com" };
// → { fName: "James", lName: "Bond", id: 1, email: "007@mi6.com" }6.3 在 React 中的应用
jsx
const contact = {
name: "Beyoncé",
img: "https://example.com/beyonce.jpg",
tel: "+123 456 789",
email: "b@email.com"
};
// 传统方式
<Card name={contact.name} img={contact.img} tel={contact.tel} email={contact.email} />
// 展开运算符 ⭐
<Card {...contact} />
// 等同于将 contact 对象的所有属性作为 props 传入添加新状态(不可变更新):
javascript
const [items, setItems] = useState([]);
// ❌ 直接修改
items.push(newItem); // 不会触发重新渲染!
// ✅ 展开 + 新元素
setItems(prev => [...prev, newItem]);七、Keeper App 项目结构
7.1 组件树
App
├── Header — 应用标题
├── Note — 笔记卡片(可复用)
├── Note
├── ...
├── CreateArea — 创建新笔记的表单
└── Footer — 版权信息7.2 文件结构
src/
├── index.js # 入口
└── components/
├── App.jsx # 根组件
├── Header.jsx # 头部
├── Footer.jsx # 页脚(动态年份)
├── Note.jsx # 笔记卡片
└── CreateArea.jsx # 创建区域7.3 组件示例
jsx
// Header.jsx
function Header() {
return (
<header>
<h1>Keeper</h1>
</header>
);
}
// Footer.jsx
function Footer() {
const year = new Date().getFullYear();
return (
<footer>
<p>Copyright ⓒ {year}</p>
</footer>
);
}
// Note.jsx
function Note({ title, content }) {
return (
<div className="note">
<h1>{title}</h1>
<p>{content}</p>
</div>
);
}八、速查表
| 概念 | 语法 |
|---|---|
| Props 传递 | <Card name="Angela" age={26} /> |
| Props 接收 | function Card(props) 或 function Card({ name, age }) |
| map 渲染 | {items.map(item => <Component key={item.id} {...item} />)} |
| filter | arr.filter(x => x > 10) |
| reduce | arr.reduce((acc, cur) => acc + cur) |
| find | arr.find(x => x > 10) |
| findIndex | arr.findIndex(x => x > 10) |
| 三元渲染 | {condition ? <A /> : <B />} |
| && 渲染 | {condition && <A />} |
| 对象解构 | const { name, age } = props |
| 数组解构 | const [a, b] = [1, 2] |
| 展开 props | <Card {...contactObj} /> |
| 展开合并 | [...oldArr, newItem] |