你经常需要从数据集合中显示多个类似的组件。你可以使用JavaScript 数组方法来操作数据数组。在此页面上,你将使用filter()
和map()
与 React 结合,将你的数据数组过滤并转换为组件数组。
你将学习
- 如何使用 JavaScript 的
map()
从数组中渲染组件 - 如何使用 JavaScript 的
filter()
仅渲染特定组件 - 何时以及为何使用 React 键
从数组中渲染数据
假设你有一份内容列表。
<ul>
<li>Creola Katherine Johnson: mathematician</li>
<li>Mario José Molina-Pasquel Henríquez: chemist</li>
<li>Mohammad Abdus Salam: physicist</li>
<li>Percy Lavon Julian: chemist</li>
<li>Subrahmanyan Chandrasekhar: astrophysicist</li>
</ul>
这些列表项之间唯一的区别是它们的内容,它们的数据。在构建界面时,你经常需要使用不同的数据显示相同组件的多个实例:从评论列表到个人资料图片库。在这些情况下,你可以将数据存储在 JavaScript 对象和数组中,并使用方法,例如map()
和filter()
来从它们渲染组件列表。
这是一个如何从数组生成项目列表的简短示例
- 移动数据到数组中
const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];
- 映射
people
成员到一个新的 JSX 节点数组,listItems
const listItems = people.map(person => <li>{person}</li>);
- 返回
listItems
,组件中包含在<ul>
中
return <ul>{listItems}</ul>;
这是结果
const people = [ 'Creola Katherine Johnson: mathematician', 'Mario José Molina-Pasquel Henríquez: chemist', 'Mohammad Abdus Salam: physicist', 'Percy Lavon Julian: chemist', 'Subrahmanyan Chandrasekhar: astrophysicist' ]; export default function List() { const listItems = people.map(person => <li>{person}</li> ); return <ul>{listItems}</ul>; }
请注意,上面的沙箱显示了一个控制台错误
你将在本页稍后学习如何修复此错误。在我们开始之前,让我们为你的数据添加一些结构。
过滤项目数组
这些数据可以结构化得更好。
const people = [{
id: 0,
name: 'Creola Katherine Johnson',
profession: 'mathematician',
}, {
id: 1,
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'chemist',
}, {
id: 2,
name: 'Mohammad Abdus Salam',
profession: 'physicist',
}, {
id: 3,
name: 'Percy Lavon Julian',
profession: 'chemist',
}, {
id: 4,
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
}];
假设你想要一种只显示职业为'chemist'
的人的方法。你可以使用 JavaScript 的filter()
方法来只返回这些人。此方法接受项目数组,将它们通过“测试”(返回true
或false
的函数),并返回一个仅包含通过测试(返回true
)的项目的新的数组。
你只需要那些profession
为 'chemist'
的项目。此项的“测试”函数如下所示:(person) => person.profession === 'chemist'
。以下是组合方法。
- 创建一个仅包含“化学家”人员的新数组
chemists
,方法是对people
调用filter()
并根据person.profession === 'chemist'
进行过滤。
const chemists = people.filter(person =>
person.profession === 'chemist'
);
- 现在,映射
chemists
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession + ' '}
known for {person.accomplishment}
</p>
</li>
);
- 最后,返回组件中的
listItems
return <ul>{listItems}</ul>;
import { people } from './data.js'; import { getImageUrl } from './utils.js'; export default function List() { const chemists = people.filter(person => person.profession === 'chemist' ); const listItems = chemists.map(person => <li> <img src={getImageUrl(person)} alt={person.name} /> <p> <b>{person.name}:</b> {' ' + person.profession + ' '} known for {person.accomplishment} </p> </li> ); return <ul>{listItems}</ul>; }
使用key
保持列表项顺序
请注意,上面所有沙箱都在控制台中显示错误。
你需要为每个数组项提供一个key
——一个字符串或数字,用于唯一标识该数组中的其他项。
<li key={person.id}>...</li>
键告诉React哪个数组项对应哪个组件,以便稍后将其匹配。如果你的数组项可以移动(例如,由于排序)、插入或删除,这将变得很重要。精心选择的key
可以帮助React推断到底发生了什么,并对DOM树进行正确的更新。
不要动态生成键,而应该将它们包含在你的数据中。
export const people = [{ id: 0, // Used in JSX as a key name: 'Creola Katherine Johnson', profession: 'mathematician', accomplishment: 'spaceflight calculations', imageId: 'MK3eW3A' }, { id: 1, // Used in JSX as a key name: 'Mario José Molina-Pasquel Henríquez', profession: 'chemist', accomplishment: 'discovery of Arctic ozone hole', imageId: 'mynHUSa' }, { id: 2, // Used in JSX as a key name: 'Mohammad Abdus Salam', profession: 'physicist', accomplishment: 'electromagnetism theory', imageId: 'bE7W1ji' }, { id: 3, // Used in JSX as a key name: 'Percy Lavon Julian', profession: 'chemist', accomplishment: 'pioneering cortisone drugs, steroids and birth control pills', imageId: 'IOjWm71' }, { id: 4, // Used in JSX as a key name: 'Subrahmanyan Chandrasekhar', profession: 'astrophysicist', accomplishment: 'white dwarf star mass calculations', imageId: 'lrWQx8l' }];
深入探讨
当每个项目需要渲染多个DOM节点时,你该怎么办?
简短的<>...</>
片段语法不允许你传递键,因此你需要将它们组合到单个<div>
中,或者使用稍长一些且更明确的<Fragment>
语法:
import { Fragment } from 'react';
// ...
const listItems = people.map(person =>
<Fragment key={person.id}>
<h1>{person.name}</h1>
<p>{person.bio}</p>
</Fragment>
);
片段会从DOM中消失,因此这将产生一个<h1>
、<p>
、<h1>
、<p>
等等的扁平列表。
从哪里获取你的key
不同的数据源提供不同的键源。
- 来自数据库的数据:如果你的数据来自数据库,你可以使用数据库键/ID,它们本质上是唯一的。
- 本地生成的数据:如果你的数据是在本地生成和持久化的(例如,笔记应用中的笔记),在创建项目时,使用递增计数器、
crypto.randomUUID()
或类似uuid
这样的包。
键的规则
- 键在同级元素中必须唯一。但是,对于不同数组中的 JSX 节点,使用相同的键是可以的。
- 键不能更改,否则会破坏其用途!不要在渲染时生成它们。
为什么 React 需要键?
想象一下,你桌面上文件没有名字。你需要按顺序来称呼它们——第一个文件,第二个文件,等等。你可以习惯它,但是一旦你删除一个文件,就会变得混乱。第二个文件会变成第一个文件,第三个文件会变成第二个文件,以此类推。
文件夹中的文件名和数组中的 JSX 键具有相似的用途。它们使我们能够唯一地识别同级元素中的项目。一个精心选择的键比数组中的位置提供更多信息。即使由于重新排序而导致位置发生变化,key
也能让 React 在其整个生命周期中识别该项目。
回顾
在本页中,你学习了
- 如何将数据从组件移到数组和对象等数据结构中。
- 如何使用 JavaScript 的
map()
生成一系列类似的组件。 - 如何使用 JavaScript 的
filter()
创建过滤后的项目数组。 - 为什么以及如何为集合中的每个组件设置
key
,以便 React 即使在组件的位置或数据更改时也能跟踪每个组件。
挑战 1的 4: 将列表分成两部分
此示例显示所有人员的列表。
将其更改为依次显示两个单独的列表:化学家和其他人。与之前一样,你可以通过检查person.profession === 'chemist'
来确定一个人是否是化学家。
import { people } from './data.js'; import { getImageUrl } from './utils.js'; export default function List() { const listItems = people.map(person => <li key={person.id}> <img src={getImageUrl(person)} alt={person.name} /> <p> <b>{person.name}:</b> {' ' + person.profession + ' '} known for {person.accomplishment} </p> </li> ); return ( <article> <h1>Scientists</h1> <ul>{listItems}</ul> </article> ); }