react学习笔记-渲染列表

渲染列表

你可能经常需要通过JavaScript的数组方法来操作数组中的数据,从而将一个数据集渲染成多个相似的组件。在这篇文章中,你将学会如何在React中使用filter()筛选需要渲染的组件和使用map()把数组转换成组件数组。

你将会学习到

-如何通过JavaScript的map()方法从数组中生成组件

-如何通过JavaScript的filter()筛选需要渲染的组件

-如何以及为何使用React中的key

从数组中渲染数据

这里我们有一个列表。

<ul>

  <li>凯瑟琳·约翰逊: 数学家</li>

  <li>马里奥·莫利纳: 化学家</li>

  <li>穆罕默德·阿卜杜勒·萨拉姆: 物理学家</li>

  <li>珀西·莱温·朱利亚: 化学家</li>

  <li>苏布拉马尼扬·钱德拉塞卡: 天体物理学家</li>

</ul>

可以看到,这些类表项之间唯一的区别就是其中的内容/数据。未来你可能会碰到很多类似的情况,在那些场景中,你想基于不同的数据渲染出相似的组件,比如评论列表或者个人资料的图库。在这样的场景下,可以把要用到的数据存入JavaScript对象或数组,然后用map()filter()这样的方法来渲染出一个组件列表。

这里给出一个由数组生成一系列列表项的简单示例:

1、首先,把数据存储到数组中:

const people = [

  '凯瑟琳·约翰逊: 数学家',

  '马里奥·莫利纳: 化学家',

  '穆罕默德·阿卜杜勒·萨拉姆: 物理学家',

  '珀西·莱温·朱利亚: 化学家',

  '苏布拉马尼扬·钱德拉塞卡: 天体物理学家'

];

2、遍历people这个数组中的每一项,并获得一个新的JSX节点数组listItems:

const listItems = people.map(person => <li>{person}</li>);

  1. 把listItems用<ul>包裹起来,然后返回它:

return <ul>{listItems}</ul>;

来看看运行的效果

App.js:

const people = [

  '凯瑟琳·约翰逊: 数学家',

  '马里奥·莫利纳: 化学家',

  '穆罕默德·阿卜杜勒·萨拉姆: 物理学家',

  '珀西·莱温·朱利亚: 化学家',

  '苏布拉马尼扬·钱德拉塞卡: 天体物理学家'

];

export default function List() {

  const listItems = people.map(person => <li>{person}</li>);

  return <ul>{listItems}</ul>;

}

index.js:

import React from 'react';

import ReactDOM from 'react-dom/client';

import './index.css';

import List from './App';  // 根据你的导出名称调整

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(

  <React.StrictMode>

    <List />  {/* 这里使用你在 App.js 中导出的组件名 */}

  </React.StrictMode>

);

注意,控制台可能会输出这个错误:

等会我们会学到怎么修复它。在此之前,我们先来看看如何把这个数组变得更加结构化。

对数组项进行过滤

让我们把people数组变得更加结构化一点。

const people = [{

  id: 0,

  name: '凯瑟琳·约翰逊',

  profession: '数学家',

}, {

  id: 1,

  name: '马里奥·莫利纳',

  profession: '化学家',

}, {

  id: 2,

  name: '穆罕默德·阿卜杜勒·萨拉姆',

  profession: '物理学家',

}, {

  id: 3,

  name: '珀西·莱温·朱利亚',

  profession: '化学家',

}, {

  id: 4,

  name: '苏布拉马尼扬·钱德拉塞卡',

  profession: '天体物理学家',

}];

现在,假设你只想在屏幕上显示职业是化学家的人。那么你可以使用JavaScript的filter()方法来返回满足条件的项。这个方法会让数组的子项经过“过滤器”(一个返回值为true或false的函数)的筛选,最终返回一个只包含满足条件的项的新数组。

既然你只想显示profession值是化学家的人,那么这里的“过滤器”函数应该长这样:(person) => person.professioni === ‘化学家’。下面我们来看看怎么把它们组合在一起:

1.首先,创建一个用来存化学家们的新数组chemists,这里用到filter()方法过滤people数组来得到所有的化学家,过滤的条件应该是person.profession === ‘化学家’:

const chemists = people.filter(person => person.profession === '化学家');

2.接下来用map方法遍历chemists数组:

const listItems = chemists.map(person =>

    <li>

      <img

        src={etImageUrl(person)}

        alt={person.name}

      />

      <p>

        <b>{person.name}:</b>

        {' ' + person.profession + ' '}

        因{person.accomplishment}而闻名世界

      </p>

    </li>

    )

3.最后,返回listItems:

return <ul>{listItems}</ul>;

App.js:

import React from 'react';

import { people } from './data.js';

import { getImageUrl } from './utils.js';

export default function List() {

  const chemists = people.filter(person => person.profession === '化学家');

  const listItems = chemists.map(person =>

    <li>

      <img

        src={getImageUrl(person)}

        alt={person.name}

      />

      <p>

        <b>{person.name}:</b>

        {' ' + person.profession + ' '}

        因{person.accomplishment}而闻名世界

      </p>

    </li>

    )

  return <ul>{listItems}</ul>;

}

data.js::

export const people = [

  {

    id: 0,

    name: '凯瑟琳·约翰逊',

    profession: '数学家',

    accomplishment: '太空飞行相关数值的核算',

    imageId: 'MK3eW3A',

  },

  {

    id: 1,

    name: '马里奥·莫利纳',

    profession: '化学家',

    accomplishment: '北极臭氧空洞的发现',

    imageId: 'mynHUSa',

  },

  {

    id: 2,

    name: '穆罕默德·阿卜杜勒·萨拉姆',

    profession: '物理学家',

    accomplishment: '关于基本粒子间弱相互作用和电磁相互作用的统一理论',

    imageId: 'bE7W1ji',

  },

  {

    id: 3,

    name: '珀西·莱温·朱利亚',

    profession: '化学家',

    accomplishment: '开创性的可的松药物、类固醇和避孕药的研究',

    imageId: 'IOjWm71',

  },

  {

    id: 4,

    name: '苏布拉马尼扬·钱德拉塞卡',

    profession: '天体物理学家',

    accomplishment: '白矮星质量计算',

    imageId: 'lrWQx8l',

  },

];

utils.js:

export function getImageUrl(person) {

  return (

    'https://i.imgur.com/' +

    person.imageId +

    's.jpg'

  );

}

index.js:

import React from 'react';

import ReactDOM from 'react-dom/client';

import './index.css';

import List from './App';  // 根据你的导出名称调整

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(

  <React.StrictMode>

    <List />  {/* 这里使用你在 App.js 中导出的组件名 */}

  </React.StrictMode>

);

index.html:

<div id="root"></div>

陷阱:

因为箭头函数会隐式地返回位于 =>之后的表达式,所以你可以省略return 语句。

const listItems = chemists.map(person =>

<li>…</li> // 隐式地返回 !

);

不过,如果你的 => 后面跟了一对花括号{,那么你必须是用return来指定返回值!

const listItems = chemists.map(person => { // 花括号

  return <li> ...</li>

});

箭头函数 => { 后面的部分被称为“块函数体”,块函数体支持多行代码的写法,但要用return语句才能指定返回值。假如你忘了写return,那这个函数什么都不会返回!

用key保持列表项顺序

如果把上面任何一个沙盒示例在新标签项打开,你就会发现控制台有这样一个报错:

这是因为你必须给数组中的每一项都指定一个key—它们可以是字符串或数字的形式,只要能唯一标识出各个数组项就行:

<li key={person.li}>…</li>

注意:

直接放在map()方法里的JSX元素一般都需要指定key值!

这些key会告诉React,每个组件对应着数组里的哪一项,所以React可以把它们匹配起来。这在数组项进行移动(例如排序)、插入或删除等操作时非常重要。一个合适的key可以帮助React推断发生了什么,从而得以正确地更新DOM树。

用作key的值应该在数据中提前就准备好,而不是在运行时才随手生成:

utils.js:

export function getImageUrl(person) {

  return (

    'https://i.imgur.com/' +

    person.imageId +

    's.jpg'

  );

}

data.js:

export const people = [

  {

    id: 0, // 在 JSX 中作为 key 使用

    name: '凯瑟琳·约翰逊',

    profession: '数学家',

    accomplishment: '太空飞行相关数值的核算',

    imageId: 'MK3eW3A',

  },

  {

    id: 1, // 在 JSX 中作为 key 使用

    name: '马里奥·莫利纳',

    profession: '化学家',

    accomplishment: '北极臭氧空洞的发现',

    imageId: 'mynHUSa',

  },

  {

    id: 2, // 在 JSX 中作为 key 使用

    name: '穆罕默德·阿卜杜勒·萨拉姆',

    profession: '物理学家',

    accomplishment: '关于基本粒子间弱相互作用和电磁相互作用的统一理论',

    imageId: 'bE7W1ji',

  },

  {

    id: 3, // 在 JSX 中作为 key 使用

    name: '珀西·莱温·朱利亚',

    profession: '化学家',

    accomplishment: '开创性的可的松药物、类固醇和避孕药',

    imageId: 'IOjWm71',

  },

  {

    id: 4, // 在 JSX 中作为 key 使用

    name: '苏布拉马尼扬·钱德拉塞卡',

    profession: '天体物理学家',

    accomplishment: '白矮星质量计算',

    imageId: 'lrWQx8l',

  },

];

App.js:

import React from 'react';

import { people } from './data.js';

import { getImageUrl } from './utils.js';

export default function List() {

  const chemists = people.filter(person => person.profession === '化学家');

  const listItems = chemists.map(person => { // 花括号

    return <li key={person.id}>

    <img

      src={getImageUrl(person)}

      alt={person.name}

    />

    <p>

      <b>{person.name}:</b>

      {' ' + person.profession + ' '}

      因{person.accomplishment}而闻名世界

    </p>

  </li>

  }

   

    )

  return <ul>{listItems}</ul>;

}

可以看到,已经不太提示缺少key的错误了

探讨-为每个列表项显示多个DOM节点

如果你想让每个类表项都输出多个DOM节点而非一个的话,该怎么做呢?

Fragment语法的简写形式<> </>无法接受key值,所以你只能要么把生成的节点用一个<div>标签包裹起来,要么使用长一点但更明确的<Fragment>写法:

import { Fragment } from 'react';

// …

const listItems = chemists.map(person =>  // 花括号

  <Fragment key={person.id}>

    <h1>{person.name}</h1>

    <p>{person.bio}</p>

  </Fragment>

 

  )

这里的Fragment标签本身并不会出现在DOM上,这串代码最终会转换成<h1>、<p>、<h1>、<p>……的列表。

如何设定key值

不同来源的数据往往对应不同的key值获取方式:

-来自数据库的数据:如果你的数据是从数据库中获取的,那么可以直接使用数据表中的主键,因为它们天然具有唯一性。

-本地产生数据:如果你数据的产生和保存都在本地(例如笔记软件里的笔记),那么你可以使用一个自增计数器,crypto.randomUUID()或者一个类似uuid的库来生成key。

Key需要满足的条件

-key值在兄弟节点之间必须是唯一的。不过不要求全局唯一,在不同的数组中可以使用相同的key。

-key值不能改变,否则就失去了使用key的意义!所以千万不要在渲染时动态地生成key。

React中为什么需要key?

设想一下,加入你桌面上的文件都没有文件名,取而代之的是,你需要通过文件的位置顺序来区分它们----第一个文件,第二个文件,以此类推。也许你也不是不能接收这种方式,可以一旦你删除了其中一个文件,这种组织方式就会变得混乱无比。原来的第二个文件可能会变成第一个文件,第三个文件会成第二个文件。。。。。

React里需要key和文件夹里的文件需要有文件名的道理是类似的。它们(key和文件名)都让我们可以从众多的兄弟元素中唯一标识出某一项(JSX节点或文件)。而一个精心选择的key值所能提供的信息远远不至于这个元素在数组中的位置。即是元素的位置在渲染的过程中发生了改变,它提供的key值也能让React在整个生命周期中一直认得它。

陷阱:

你可能会想直接把数组项的索引当做key值来用,实际上,如果你没有显示地指定key值,React确实默认会这么做。但是数组项的顺序在插入、删除或者重新排序等操作中会发生改变,此时把索引顺序用作key值会产生一些微妙且令人困惑的bug。

之一类似,请不要在运行过程中动态地产生key,像是key={Math.random()}这种方式。这会导致每次重新渲染后的key值都不一样,从而使得所有的组件和DOM元素每次都需要重新创建。这不仅会造成运行变慢的问题,更有可能导致用户输入的丢失。所以,使用能从给定数据中稳定取得的值才是明智的选择。

有一点需要注意,组件不会把key当做props的一部分。Key的存在只对React本身起到提示作用。如果你的组件需要一个ID,那么请把它作为一个单独的prop传给组件: <Profile key={id} useid={id}></Profile>。

摘要

在这篇文章中,你学习到了:

-如果从组件中抽离出数据,并把它们放入像数组、对象这样的数据结构中。

-如果使用JavaScript的map()方法来生成一个类似的组件。

-如果使用JavaScript的filter()方法来筛选数组。

-如何以及如何给集合中的每个组件设置一个key值:它使React能追踪这些组件,即便后者的位置或数据发生了变化。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值