你通常希望显示数据集合中的多个相似组件。你可以使用 JavaScript 数组方法 来操作数据数组。在此页面上,你将使用 filter()
和 map()
以及 React 来过滤数据数组并将其转换为组件数组。
¥You will often want to display multiple similar components from a collection of data. You can use the JavaScript array methods to manipulate an array of data. On this page, you’ll use filter()
and map()
with React to filter and transform your array of data into an array of components.
你将学习到
-
如何使用 JavaScript 的
map()
从数组中渲染组件¥How to render components from an array using JavaScript’s
map()
-
如何使用 JavaScript 的
filter()
只渲染特定组件¥How to render only specific components using JavaScript’s
filter()
-
何时以及为何使用 React 键
¥When and why to use React keys
从数组中渲染数据
¥Rendering data from arrays
假设你有一个内容列表。
¥Say that you have a list of content.
<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()
等方法从中渲染组件列表。
¥The only difference among those list items is their contents, their data. You will often need to show several instances of the same component using different data when building interfaces: from lists of comments to galleries of profile images. In these situations, you can store that data in JavaScript objects and arrays and use methods like map()
and filter()
to render lists of components from them.
以下是如何从数组生成条目列表的简短示例:
¥Here’s a short example of how to generate a list of items from an array:
-
将数据移动到数组中:
¥Move the data into an array:
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
:¥Map the
people
members into a new array of JSX nodes,listItems
:
const listItems = people.map(person => <li>{person}</li>);
-
从封装在
<ul>
中的组件返回listItems
:¥Return
listItems
from your component wrapped in a<ul>
:
return <ul>{listItems}</ul>;
这是结果:
¥Here is the result:
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>; }
请注意上面的沙箱显示控制台错误:
¥Notice the sandbox above displays a console error:
稍后你将在本页了解如何修复此错误。在开始之前,让我们为你的数据添加一些结构。
¥You’ll learn how to fix this error later on this page. Before we get to that, let’s add some structure to your data.
过滤条目数组
¥Filtering arrays of items
这些数据甚至可以更加结构化。
¥This data can be structured even more.
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
)。
¥Let’s say you want a way to only show people whose profession is 'chemist'
. You can use JavaScript’s filter()
method to return just those people. This method takes an array of items, passes them through a “test” (a function that returns true
or false
), and returns a new array of only those items that passed the test (returned true
).
你只需要 profession
为 'chemist'
的条目。“测试” 函数看起来像 (person) => person.profession === 'chemist'
。以下是如何将它们放在一起:
¥You only want the items where profession
is 'chemist'
. The “test” function for this looks like (person) => person.profession === 'chemist'
. Here’s how to put it together:
-
通过在
person.profession === 'chemist'
过滤的people
上调用filter()
,创建一个由“化学家”人员组成的新数组chemists
:¥Create a new array of just “chemist” people,
chemists
, by callingfilter()
on thepeople
filtering byperson.profession === 'chemist'
:
const chemists = people.filter(person =>
person.profession === 'chemist'
);
-
现在映射
chemists
:¥Now map over
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
:¥Lastly, return the
listItems
from your component:
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
保持列表项的顺序
¥Keeping list items in order with key
请注意,上面的所有沙箱都在控制台中显示错误:
¥Notice that all the sandboxes above show an error in the console:
你需要为每个数组项提供一个 key
一个字符串或一个数字,用于在该数组的其他项中唯一标识它:
¥You need to give each array item a key
— a string or a number that uniquely identifies it among other items in that array:
<li key={person.id}>...</li>
键告诉 React 每个组件对应于哪个数组项,以便稍后匹配它们。如果你的数组项可以移动(例如由于排序)、插入或删除,这就变得很重要。精心选择的 key
帮助 React 推断到底发生了什么,并对 DOM 树进行正确的更新。
¥Keys tell React which array item each component corresponds to, so that it can match them up later. This becomes important if your array items can move (e.g. due to sorting), get inserted, or get deleted. A well-chosen key
helps React infer what exactly has happened, and make the correct updates to the DOM tree.
与其即时生成键,不如将它们包含在数据中:
¥Rather than generating keys on the fly, you should include them in your data:
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' }];
深入研究
¥Displaying several DOM nodes for each list item
当每个条目需要渲染的不是一个而是多个 DOM 节点时,你会怎么做?
¥What do you do when each item needs to render not one, but several DOM nodes?
短的 <>...</>
分段 语法不允许你传递键,因此你需要将它们组合成一个 <div>
,或者使用稍长的 更明确的 <Fragment>
语法:
¥The short <>...</>
Fragment syntax won’t let you pass a key, so you need to either group them into a single <div>
, or use the slightly longer and more explicit <Fragment>
syntax:
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>
等的扁平列表。
¥Fragments disappear from the DOM, so this will produce a flat list of <h1>
, <p>
, <h1>
, <p>
, and so on.
何处可以获取你的 key
¥Where to get your key
不同的数据来源提供不同的键来源:
¥Different sources of data provide different sources of keys:
-
来自数据库的数据:如果你的数据来自数据库,你可以使用本质上唯一的数据库键/ID。
¥Data from a database: If your data is coming from a database, you can use the database keys/IDs, which are unique by nature.
-
本地生成的数据:如果你的数据是在本地生成和保存的(例如注意应用中的注意),请在创建项目时使用递增计数器、
crypto.randomUUID()
或类似uuid
的包。¥Locally generated data: If your data is generated and persisted locally (e.g. notes in a note-taking app), use an incrementing counter,
crypto.randomUUID()
or a package likeuuid
when creating items.
键的规则
¥Rules of keys
-
键在兄弟项中必须是唯一的。但是,可以为不同数组中的 JSX 节点使用相同的键。
¥Keys must be unique among siblings. However, it’s okay to use the same keys for JSX nodes in different arrays.
-
键不得更改,否则就达不到其目的!不要在渲染时生成它们。
¥Keys must not change or that defeats their purpose! Don’t generate them while rendering.
为什么 React 需要键?
¥Why does React need keys?
想象一下你桌面上的文件没有名称。而是,你可以按顺序引用它们 - 第一个文件、第二个文件,依此类推。你可以习惯它,但是一旦你删除了一个文件,它就会变得混乱。第二个文件将成为第一个文件,第三个文件将成为第二个文件,依此类推。
¥Imagine that files on your desktop didn’t have names. Instead, you’d refer to them by their order — the first file, the second file, and so on. You could get used to it, but once you delete a file, it would get confusing. The second file would become the first file, the third file would be the second file, and so on.
文件夹中的文件名和数组中的 JSX 键具有相似的用途。它们让我们在其兄弟项之间唯一地识别一个项目。精心选择的键提供的信息比数组中的位置更多。即使位置因重新排序而发生变化,key
也让 React 在其整个生命周期内识别该条目。
¥File names in a folder and JSX keys in an array serve a similar purpose. They let us uniquely identify an item between its siblings. A well-chosen key provides more information than the position within the array. Even if the position changes due to reordering, the key
lets React identify the item throughout its lifetime.
回顾
在此页面上,你了解到:
¥On this page you learned:
-
如何将数据从组件中移出并移入数组和对象等数据结构中。
¥How to move data out of components and into data structures like arrays and objects.
-
如何使用 JavaScript 的
map()
生成一组相似的组件。¥How to generate sets of similar components with JavaScript’s
map()
. -
如何使用 JavaScript 的
filter()
创建过滤条目数组。¥How to create arrays of filtered items with JavaScript’s
filter()
. -
为何以及如何在集合中的每个组件上设置
key
,以便 React 可以跟踪每个组件,即使它们的位置或数据发生变化。¥Why and how to set
key
on each component in a collection so React can keep track of each of them even if their position or data changes.
挑战 1 / 4: 将列表一分为二
¥Splitting a list in two
此示例显示所有人的列表。
¥This example shows a list of all people.
将其更改为依次显示两个单独的列表:化学家和其他人。与之前一样,你可以通过检查 person.profession === 'chemist'
是否为化学家来确定一个人是否是化学家。
¥Change it to show two separate lists one after another: Chemists and Everyone Else. Like previously, you can determine whether a person is a chemist by checking if 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> ); }