这篇解决三个问题:怎么把数组渲染成一排标签、为什么要有 Key、以及怎么处理用户的点击输入等事件。
一、渲染列表
实际开发中经常遇到这种情况:后端给你一个数组,比如水果列表['苹果', '香蕉', '橘子'],你需要在页面上显示成一排列表项。
这时候用数组的map()方法:遍历数组每一项,每一项返回一个 JSX 元素。
function FruitList() { const fruits = ["苹果", "香蕉", "橘子"]; return ( <ul> {fruits.map((fruit) => ( <li>{fruit}</li> ))} </ul> ); }外面包一层{}是因为要在 JSX 里写 JavaScript 表达式。map 遍历每一个水果,生成一个<li>标签,React 自动帮你拼成一排。
如果数组里是对象呢?比如商品列表:
function ProductList() { const products = [ { id: 1, name: "手机", price: 3999 }, { id: 2, name: "电脑", price: 8999 }, { id: 3, name: "耳机", price: 999 }, ]; return ( <div> {products.map((product) => ( <div className="product"> <h3>{product.name}</h3> <p>价格:¥{product.price}</p> </div> ))} </div> ); }二、列表的 Key 键
上面的代码跑起来其实会有警告。打开浏览器控制台,你会看到一条黄色的警告:“Each child in a list should have a unique key prop”。
2.1 Key 是干嘛的?
想象你排队买奶茶,每个人都有个号码牌。队伍顺序变了(有人插队、有人走了),工作人员靠号码牌认出 “哦这是刚才那个人”,而不是把所有人都重新认一遍。
Key 就是列表项的号码牌。React 更新列表的时候,靠 Key 判断哪一项是新的、哪一项动过了,这样只改变化的部分,性能更好。
正确写法是给每个列表项加一个key属性:
function ProductList() { const products = [ { id: 1, name: "手机", price: 3999 }, { id: 2, name: "电脑", price: 8999 }, { id: 3, name: "耳机", price: 999 }, ]; return ( <div> {products.map((product) => ( <div key={product.id} className="product"> <h3>{product.name}</h3> <p>价格:¥{product.price}</p> </div> ))} </div> ); }用数据里的唯一 id 当 key,就不会报警告了。
2.2 为什么不能用 index 当 Key?
新手很容易图省事,直接用数组下标当 key:
// ❌ 不推荐 {fruits.map((fruit, index) => ( <li key={index}>{fruit}</li> ))}为什么不好?举个例子:队伍有三个人 [A, B, C],下标分别是 0、1、2。现在 A 走了,变成 [B, C]。
如果用下标当 key:B 原来是下标 1,现在变成下标 0 了。React 会以为 “哦下标 0 的内容从 A 变成 B 了,下标 1 从 B 变成 C 了,下标 2 的 C 没了”。结果三个元素全部重新渲染,性能浪费。
而且更严重的是,如果列表项里面有输入框、有自己的状态,顺序一变状态就会错乱。
记住:Key 必须用数据本身的唯一标识,比如 id,不能用数组下标。
只有一种情况可以用 index:列表是纯静态的,永远不会增删排序。但这种情况很少。
三、组件样式怎么加
React 里给组件加样式,有三种常用方式。
3.1 行内样式
直接在标签上写style属性,注意要写成对象:
function Button() { return ( <button style={{ color: "white", backgroundColor: "blue", padding: "8px 16px" }}> 点我 </button> ); }适合临时、动态的样式。如果样式固定,还是写外部 CSS 比较好。
3.2 外部 CSS 文件
最常用的方式。写一个.css文件,然后在组件里 import 进来。
/\* Button.css \*/ .button { color: white; background-color: blue; padding: 8px 16px; border: none; border-radius: 4px; }import "./Button.css"; function Button() { return <button className="button">点我</button>; }缺点是所有组件共享同一个 CSS 作用域,A 组件的样式可能不小心影响 B 组件。
3.3 CSS Modules
为了解决样式污染问题,可以用 CSS Modules。把文件名改成Button.module.css:
/\* Button.module.css \*/ .button { color: white; background-color: blue; padding: 8px 16px; }然后这样用:
import styles from "./Button.module.css"; function Button() { return <button className={styles.button}>点我</button>; }这样每个组件的样式自动加了后缀,互不干扰,相当于 “样式私有化”。
四、事件处理
用户点按钮、输入文字、滚动页面,这些都是事件。React 处理事件的方式和原生 JS 很像,但有几个区别。
4.1 基本写法
function Button() { function handleClick() { alert("你点了我"); } return <button onClick={handleClick}>点我</button>; }注意几个关键点:
事件名用驼峰:
onClick而不是onclick传函数而不是字符串:
onClick={handleClick}而不是onclick="handleClick()"不要加括号:写
onClick={handleClick}是把函数传进去,点击的时候才执行。如果写成onClick={handleClick()},就变成页面一加载就立刻执行了
4.2 传参数
如果事件处理函数需要参数,怎么办?不能直接写括号,那会立刻执行。要用箭头函数包一层:
function TodoList() { function handleDelete(id) { console.log("删除了", id); } return ( <div> <button onClick={() => handleDelete(1)}>删除第1条</button> <button onClick={() => handleDelete(2)}>删除第2条</button> </div> ); }箭头函数里调用 handleDelete,把参数传进去。这样只有点击的时候才执行。
本篇小结
渲染列表用
map()遍历数组,每一项返回一个 JSX 元素每个列表项必须加
key属性,用数据的唯一 id,不能用 index加样式三种方式:行内 style 对象、外部 CSS、CSS Modules
事件处理:事件名驼峰、传函数不加括号、要传参数用箭头函数包一层