渲染列表
您通常希望显示数据集合中的多个相似组件。 您可以使用 JavaScript 数组方法来操作数据数组。 在此页面上,您将结合使用 filter() 和 map() 和 React 来过滤数据数组并将其转换为组件数组。
你将学习
- 如何使用 JavaScript 的 map() 从数组中渲染组件
- 如何使用 JavaScript 的 filter() 只渲染特定的组件
- 何时以及为何使用 React 键key
从数组中渲染数据
假设您有一个内容列表。
<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>);
3.从包裹在 <ul> 中的组件返回 listItems:
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>;
}
请注意上面的沙箱显示控制台错误:
Console
X Warning: Each child in a list should have a unique “key” prop.
您将在本页稍后了解如何修复此错误。 在这之前,让我们为您的数据添加一些结构。
过滤数组的数据项
这些数据甚至可以更加结构化。
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',
}, {
name: 'Percy Lavon Julian',
profession: 'chemist',
}, {
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
}];
假设您想要一种方法来只显示职业是“化学家chemist”的人。 您可以使用 JavaScript 的 filter() 方法只返回那些人。 此方法采用一组项目,通过“测试”(返回 true 或 false 的函数)传递它们,并返回仅包含通过测试(返回 true)的那些项目的新数组。
你只想要职业是“ chemist”的物品。 这个“测试”函数看起来像 (person) => person.profession === 'chemist'。 以下是如何将它们放在一起:
1.通过对 person.profession === 'chemist' 过滤的人员调用 filter() 来创建一个仅包含“化学家”人员、化学家的新数组:
const chemists = people.filter(person =>
person.profession === 'chemist'
);
2.现在映射化学家:
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>
);
3.最后,从您的组件返回 listItems:
return <ul>{listItems}</ul>;
utils.js
export function getImageUrl(person) {
return (
'https://i.imgur.com/' +
person.imageId +
's.jpg'
);
}
data.js
export const people = [{
id: 0,
name: 'Creola Katherine Johnson',
profession: 'mathematician',
accomplishment: 'spaceflight calculations',
imageId: 'MK3eW3A'
}, {
id: 1,
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'chemist',
accomplishment: 'discovery of Arctic ozone hole',
imageId: 'mynHUSa'
}, {
id: 2,
name: 'Mohammad Abdus Salam',
profession: 'physicist',
accomplishment: 'electromagnetism theory',
imageId: 'bE7W1ji'
}, {
id: 3,
name: 'Percy Lavon Julian',
profession: 'chemist',
accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',
imageId: 'IOjWm71'
}, {
id: 4,
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
accomplishment: 'white dwarf star mass calculations',
imageId: 'lrWQx8l'
}];
App.js
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>;
}
"陷阱
箭头函数在 => 之后隐式返回表达式,所以你不需要 return 语句:
const listItems = chemists.map(person => <li>...</li> // Implicit return! );
但是,如果 => 后跟 { 大括号,则必须显式地写 return!
const listItems = chemists.map(person => { // Curly brace return <li>...</li>; });
包含 => { 的箭头函数据说有一个“块体”。 他们让你写不止一行代码,但你必须自己写一个返回语句。 如果您忘记了它,将一无所获!
使用键按顺序排列列表项
请注意,上面的所有沙箱都在控制台中显示错误:
Console
X Warning: Each child in a list should have a unique “key” prop.
您需要为每个数组项提供一个键——一个字符串或一个数字,用于在该数组的其他项中唯一标识它:
<li key={person.id}>...</li>
注意
直接在 map() 调用中的 JSX 元素总是需要key!
Key告诉 React 每个组件对应于哪个数组项,以便稍后匹配它们。 如果您的数组项可以移动(例如由于排序)、插入或删除,这就变得很重要。 一个精心选择的键可以帮助 React 推断到底发生了什么,并对 DOM 树进行正确的更新。
与其即时生成key,不如将它们包含在数据中:
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 <ul>{listItems}</ul>;
}
深度阅读:为每个列表项显示多个 DOM 节点
当每个项目需要呈现的不是一个而是多个 DOM 节点时,您会怎么做?
简短的 <>...</> 片段语法不允许您传递key,因此您需要将它们分组到一个 <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 的包。
key的法则
- key在兄弟姐妹中必须是唯一的。 但是,可以为不同数组中的 JSX 节点使用相同的键。
- key不得更改,否则会破坏其目的! 不要在渲染时生成它们。
为何react需要key
想象一下你桌面上的文件没有名字。 相反,您可以按顺序引用它们——第一个文件、第二个文件,依此类推。 你可以习惯它,但是一旦你删除了一个文件,它就会变得混乱。 第二个文件将成为第一个文件,第三个文件将成为第二个文件,依此类推。
文件夹中的文件名和数组中的 JSX 键具有相似的用途。 它们让我们在其兄弟姐妹之间唯一地识别一个项目。 精心选择的键提供的信息比数组中的位置更多。 即使位置由于重新排序而改变,键也让 React 在其整个生命周期中识别项目。
"陷阱
您可能会想使用数组中项目的索引作为其键。 事实上,如果你根本不指定键,React 就会使用它。 但是如果一个项目被插入、删除或者数组被重新排序,你渲染项目的顺序会随着时间的推移而改变。 索引作为键通常会导致微妙且令人困惑的错误。
同样,不要即时生成key,例如 使用 key={Math.random()}。 这将导致键在渲染之间永远不会匹配,从而导致每次都重新创建所有组件和 DOM。 这不仅速度慢,而且还会丢失列表项内的任何用户输入。 相反,使用基于数据的稳定 ID。
请注意,您的组件不会接收key作为prop。 它仅用作 React 本身的提示。 如果你的组件需要一个 ID,你必须将它作为一个单独的 prop 传递:<Profile key={id} userId={id} />。
回顾
在此页面上,您了解到:
- 如何将数据从组件中移出并移入数组和对象等数据结构中。
- 如何使用 JavaScript 的 map() 生成一组相似的组件。
- 如何使用 JavaScript 的 filter() 创建过滤项目数组。
- 为什么以及如何在集合中的每个组件上设置键,以便 React 可以跟踪每个组件,即使它们的位置或数据发生变化。
网友评论