本文转载自:众成翻译
译者:iOSDevLog
链接:http://www.zcfy.cc/article/3826
原文:https://www.fullstackreact.com/30-days-of-react/day-13/
今天,我们将通过如何显示多个组件来准备将外部数据引入我们的应用。
我们已经构建了一个没有任何外部数据的基本应用。在我们实现之前 (我们将在明天开始这个功能),让我们来看看过去两周中我们所掩盖的事情:
重复元素
我们已经看到了这之前,我们已经遍历了一个对象列表,并在屏幕上呈现多个组件。在我们的应用中添加太多的复杂度来加载外部数据之前,今天我们将快速了解如何在应用中重复组件/元素。
由于 jsx 被浏览器视为纯 javascript 的,我们可以使用任何 传统 javascript 内的模板标签中的 jsx。我们已经看到了这一行动。作为一个快速演示:
const App = (props) => { return ( <ul> {a.map(i => { return <li>{i}</li> })} </ul> ) }
注意模板标签{}
内的内容看起来简单的 javascript。那是因为它 只是 javascript。此功能允许我们使用 (大多数) 的 javascript 在我们的模板标签 包括 原生迭代器,如 map
和 forEach
。
让我们来看看这是什么意思。让我们将上一个示例的 a
值从单个整数转换为整数列表:
const a = [1,10,100,1000,10000];
我们可以将 a
变量映射到我们的组件中,并返回将为我们构建虚拟 DOM 的响应组件列表。
const App = (props) => { return ( <ul> {a.map(i => { return <li>{i}</li> })} </ul> ) }
什么是
map()
函数?
map()
函数是数组中一个原生javascript内置函数的。
它接受在数组的每个元素上运行的函数,因此,上面的函数将运行四次,其值为i
开始为1
,然后它将再次运行它的第二个值,i
将被设置为10
等等等等。
让我们更新我们在第12天创建的应用与我们的 App
组件在这里。让我们打开我们的src/App.js
文件,并将 App
组件的内容替换为此源。清理一些未使用的变量和您的src/App.js
应该类似于以下内容:
import React from 'react'; const a = [1,10000]; const App = (props) => { return ( <ul> {a.map(i => { return <li>{i}</li> })} </ul> ) } export default App
使用create-react-app
命令生成的命令再次启动应用:npm start
,我们可以看到应用在浏览器中工作!
但是,如果我们打开开发人员控制台,我们将看到打印出错误。此错误是由以下事实引起的: React不知道如何跟踪我们列表中的各个组件,因为它们只是看起来像一个 <li />
组件。
出于性能原因,React使用虚拟 DOM 尝试限制在重新视图时需要更新的 DOM 元素的数量。如果没有任何变化,React不会使浏览器更新任何东西以节省工作。
此功能非常适合于构建 web 应用,但有时我们必须通过为节点提供唯一标识符来帮助做出React。在映射列表和渲染组件是其中之一。
React要求我们通过使用特殊属性来识别惟一的组件,这是列表中每个元素的 key
属性。key
属性可以是任何我们想要的,但它 必须是唯一的 的元素。在我们的示例中,我们可以在 map 中使用i
变量,因为数组中没有其他元素具有相同的值。
让我们更新映射来设置key:
const App = (props) => { return ( <ul> {a.map(i => { return <li key={i}>{i}</li> })} </ul> ) }
子组件
我们在本周早些时候谈到了建立父子关系的事情,但是让我们更详细地介绍一下如何访问父组件中的子组件,以及如何呈现它们。
在第11天,我们构建了一个 <Formatter />
组件来处理时钟组件中的日期格式,以使用户能够灵活地使用自己的自定义时钟渲染。回想一下,我们所创建的实现实际上是相当丑陋和相对复杂的。
const Formatter = (props) => { let children = props.format.split('').map((e,idx) => { if (e === 'h') { return <Hour key={idx} {...props} /> } else if (e === 'm') { return <Minute key={idx} {...props} /> } else if (e === 's') { return <Second key={idx} {...props} /> } else if (e === 'p') { return <Ampm key={idx} {...props} /> } else if (e === ' ') { return <span key={idx}> </span>; } else { return <Separator key={idx} {...props} /> } }); return <span>{children}</span>; }
我们可以用React.Children
对象来映射一个React对象的列表,并让React做这个自举。其结果是一个更干净的 Formatter
组件 (不是完美的,但可使用的):
const Formatter = ({format,state}) => { let children = format.split('').map(e => { if (e == 'h') { return <Hour /> } else if (e == 'm') { return <Minute /> } else if (e == 's') { return <Second /> } else if (e == 'p') { return <Ampm /> } else if (e == ' ') { return <span> </span>; } else { return <Separator /> } }); return (<span> {React.Children .map(children,c => React.cloneElement(c,state))} </span>) }
React.cloneElement
我们还没有谈论
React.cloneElement()
函数,所以让我们简单地看看它。
记得 WWWWWAAAAAYYYYY(way) 回到第2天,我们看了如何浏览器 看待 JSX? 它把它变成了类似于如下的 javascript:React.createElement("div",null,React.createElement("img",{src: "profile.jpg",alt: "Profile photo"}),React.createElement("h1","Welcome back Ari") );
React.cloneElement()
为我们处理这个。有时我们会想复制它或添加自定义props/children的组件,而不是创建一个新的组件实例 (如果我们已经有一个)。所以我们可以保留它创建的相同的属性。我们可以用React.cloneElement()
来为我们处理这一切。
React.cloneElement()
和React.createElement()
函数有相同的api,参数所在的位置:
- 我们要克隆的 ReactElement
- 我们要添加到实例中的任何
props
- 我们希望它有任何
children
在我们的
Formatter
示例中,我们正在创建列表中所有子级 (<Hour />
,<Minute />
等组件) 并将state
对象作为其属性。
React.Children
的对象提供了一些很好的实用工具来处理children的函数。上面的 Formatter
示例使用 map
函数循环访问子级,并在列表中克隆每一个。它为每一个创建一个key
(如果需要),使我们不必自己管理唯一性。
让我们使用React.Children.map()
函数更新我们的应用组件:
const App = (props) => { return ( <ul> {React.Children.map(a,i => <li>{i}</li>)} </ul> ) }
在浏览器中,一切仍然正常。
在 React.Children
中还有其他一些非常有用的方法,我们将主要使用React.Children.map()
函数,但了解其他可用的 对我们来说也是很好的。 点击文档 查看更多列表。
通过这一点,我们只处理了本地数据,而不是真正关注远程数据 (尽管我们在构建活动提要组件时 已经 简要地提到了它)。明天,我们将进入与服务器的互动,所以我们可以使用它在我们的React应用。
今天的工作很棒!