如何在React中使用Props创建包装器组件
作为 Write for DOnations 计划的一部分,作者选择了 Creative Commons 来接受捐赠。
介绍
在本教程中,您将使用 React JavaScript 库 创建带有 props 的包装器组件。 Wrapper components 是围绕未知组件的组件,并提供默认结构来显示子组件。 此模式对于创建在整个设计中重复使用的用户界面 (UI) 元素很有用,例如模式、模板页面和信息图块。
要创建包装器组件,您将首先学习使用 休息和扩展运算符 来收集未使用的道具以传递给嵌套组件。 然后,您将创建一个组件,该组件使用内置的 children 组件将嵌套组件包装在 JSX 中,就像它们是 HTML 元素一样。 最后,您将组件作为道具传递以创建灵活的包装器,这些包装器可以将自定义 JSX 嵌入到组件的多个位置。
在本教程中,您将构建组件以卡片形式显示动物数据列表。 在创建灵活的包装组件时,您将学习拆分数据和重构组件。 在本教程结束时,您将拥有一个工作应用程序,该应用程序将使用高级道具技术创建可重用的组件,这些组件将随着您的应用程序的增长和变化而扩展和适应。
注意:第一步设置一个空白项目,您将在该项目上构建教程练习。 如果您已经有一个工作项目并想直接使用道具,请从 Step 2 开始。
先决条件
- 您将需要一个运行 Node.js 的开发环境; 本教程在 Node.js 版本 10.20.1 和 npm 版本 6.14.4 上进行了测试。 要在 macOS 或 Ubuntu 18.04 上安装它,请按照 如何在 macOS 上安装 Node.js 和创建本地开发环境中的步骤或 的 使用 PPA 部分安装如何在 Ubuntu 18.04 上安装 Node.js。
- 在本教程中,您将使用 Create React App 创建一个应用程序。 您可以在 How To Set Up a React Project with Create React App 找到使用 Create React App 安装应用程序的说明以及有关其工作原理的一般信息。
- 您将使用 React 组件,您可以在我们的 How To Create Custom Components in React 教程中了解这些内容。 它还有助于对 React props 有一个基本的了解,您可以在 如何使用 Props 自定义 React 组件 中了解。
- 您还需要 JavaScript 的基本知识,您可以在我们的 如何在 JavaScript 系列中找到这些知识,以及 HTML 和 CSS 的基本知识。 HTML 和 CSS 的一个很好的资源是 Mozilla 开发者网络 。
第 1 步——创建一个空项目
在这一步中,您将使用 Create React App 创建一个新项目。 然后,您将删除引导项目时安装的示例项目和相关文件。 最后,您将创建一个简单的文件结构来组织您的组件。 这将为您在下一步构建本教程的包装应用程序提供坚实的基础。
首先,创建一个新项目。 在命令行中,运行以下脚本以使用 create-react-app 安装新项目:
npx create-react-app wrapper-tutorial
项目完成后,进入目录:
cd wrapper-tutorial
在新的终端选项卡或窗口中,使用 Create React App 启动脚本 启动项目。 浏览器将自动刷新更改,因此请在您工作时保持此脚本运行:
npm start
您将获得一个正在运行的本地服务器。 如果项目没有在浏览器窗口中打开,您可以使用 http://localhost:3000/ 打开它。 如果您从远程服务器运行它,地址将是 http://your_domain:3000。
您的浏览器将加载一个简单的 React 应用程序,该应用程序包含在 Create React App 中:
您将构建一组全新的自定义组件,因此您需要首先清除一些样板代码,以便您可以拥有一个空项目。
首先,在文本编辑器中打开 src/App.js。 这是注入页面的根组件。 所有组件都将从这里开始。 你可以在 How To Set Up a React Project with Create React App 中找到有关 App.js 的更多信息。
使用以下命令打开 src/App.js:
nano src/App.js
你会看到一个像这样的文件:
包装教程/src/App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
删除行 import logo from './logo.svg';。 然后替换 return 语句中的所有内容以返回一组空标签:<></>。 这将为您提供一个不返回任何内容的有效页面。 最终代码将如下所示:
包装教程/src/App.js
import React from 'react';
import './App.css';
function App() {
return <></>;
}
export default App;
保存并退出文本编辑器。
最后,删除标志。 您不会在应用程序中使用它,您应该在工作时删除未使用的文件。 从长远来看,它将使您免于困惑。
在终端窗口中键入以下命令:
rm src/logo.svg
如果您查看浏览器,您将看到一个空白屏幕。
现在您已经清除了示例 Create React App 项目,创建一个简单的文件结构。 这将帮助您保持组件的独立性和独立性。
在 src 目录下创建一个名为 components 的目录。 这将包含您所有的自定义组件。
mkdir src/components
每个组件都有自己的目录来存储组件文件以及样式、图像(如果有的话)和测试。
为 App 创建一个目录:
mkdir src/components/App
将所有 App 文件移动到该目录中。 使用通配符 * 选择以 App. 开头的任何文件,无论文件扩展名如何。 然后使用 mv 命令将它们放入新目录:
mv src/App.* src/components/App
接下来,更新index.js中的相对导入路径,这是引导整个过程的根组件:
nano src/index.js
导入语句需要指向App目录下的App.js文件,所以做如下高亮修改:
包装教程/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App/App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
保存并退出文件。
现在项目已经建立,您可以创建您的第一个组件。
第 2 步 — 使用 ...props 收集未使用的道具
在此步骤中,您将创建一个组件来显示一组动物的一组数据。 您的组件将包含第二个嵌套组件,以直观地显示一些信息。 要连接父组件和嵌套组件,您将使用 剩余和扩展运算符 将未使用的道具从父组件传递给子组件,而父组件无需知道道具的名称或类型。
在这一步结束时,您将拥有一个父组件,它可以为嵌套组件提供 props,而无需知道 props 是什么。 这将使父组件保持灵活性,允许您更新子组件而无需更改父组件。
创建 AnimalCard 组件
首先,为您的动物创建一组数据。 首先,在components/App目录下打开一个包含数据集的文件:
nano src/components/App/data.js
添加以下数据:
src/components/App/data.js
export default [
{
name: 'Lion',
scientificName: 'Panthero leo',
size: 140,
diet: ['meat']
},
{
name: 'Gorilla',
scientificName: 'Gorilla beringei',
size: 205,
diet: ['plants', 'insects']
},
{
name: 'Zebra',
scientificName: 'Equus quagga',
size: 322,
diet: ['plants'],
}
]
此动物列表是 objects 的 array,其中包括动物的名称、学名、体重和饮食。
保存并关闭文件。
接下来,为 AnimalCard 组件创建一个目录:
mkdir src/components/AnimalCard
在目录中打开一个新文件:
nano src/components/AnimalCard/AnimalCard.js
现在添加一个组件,将 name、diet 和 size 作为道具并显示它:
包装教程/src/components/AnimalCard/AnimalCard.js
import React from 'react';
import PropTypes from 'prop-types';
export default function AnimalCard({ diet, name, size }) {
return(
<div>
<h3>{name}</h3>
<div>{size}kg</div>
<div>{diet.join(', ')}.</div>
</div>
)
}
AnimalCard.propTypes = {
diet: PropTypes.arrayOf(PropTypes.string).isRequired,
name: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
}
在这里,您 解构 AnimalCard 函数的参数列表中的道具,然后在 div 中显示数据。 使用 join() 方法 将 diet 数据列为单个字符串。 每条数据都包含一个对应的PropType,以确保数据类型正确。
保存并关闭文件。
现在您已经有了组件和数据,您需要将它们组合在一起。 为此,将组件和数据导入项目的根组件:App.js。
首先,打开组件:
nano src/components/App/App.js
从那里,您可以循环数据并返回带有相关道具的新 AnimalCard 。 将突出显示的行添加到 App.js:
包装教程/src/components/App/App.js
import React from 'react';
import './App.css';
import animals from './data';
import AnimalCard from '../AnimalCard/AnimalCard';
function App() {
return (
<div className="wrapper">
{animals.map(animal =>
<AnimalCard
diet={animal.diet}
key={animal.name}
name={animal.name}
size={animal.size}
/>
)}
</div>
);
}
export default App;
保存并关闭文件。
当您处理更复杂的项目时,您的数据将来自更多不同的地方,例如 APIs、localStorage 或静态文件。 但是使用它们的过程将是相似的:将数据分配给一个变量并循环数据。 在这种情况下,数据来自静态文件,因此您直接导入到变量中。
在此代码中,您使用 .map() 方法迭代 animals 并显示道具。 请注意,您不必使用每条数据。 例如,您没有明确传递 scientificName 属性。 您还添加了一个单独的 key 属性,React 将使用它来 跟踪映射数据 。 最后,您将使用 div 和 wrapper 的 className 来包装代码,您将使用它来添加一些样式。
要添加此样式,请打开 App.css:
nano src/components/App/App.css
删除样板样式并将 flex 属性 添加到名为 wrapper 的类中:
道具教程/src/components/App/App.css
.wrapper {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 20px;
}
这将使用 flexbox 布局来组织数据,使其对齐。 padding 在浏览器窗口中提供了一些空间,而 justify-content 在元素之间展开了额外的空间。
保存并退出文件。 当你这样做时,浏览器会刷新,你会看到一些数据被隔开。
创建详细信息组件
您现在有一个显示数据的简单组件。 但是,假设您想通过将文本转换为表情符号来赋予 diet 数据一点天赋。 您可以通过转换组件中的数据来做到这一点。
React 被设计为灵活的,因此当您考虑如何转换数据时,您有几个不同的选择:
- 您可以在组件内创建一个函数,将文本转换为表情符号。
- 您可以创建一个函数并将其存储在组件外部的文件中,以便您可以跨不同组件重用逻辑。
- 您可以创建一个单独的组件,将文本转换为表情符号。
每种方法都适用于正确的用例,并且您会发现自己在构建应用程序时在它们之间切换。 为了避免过早的抽象和复杂性,您应该使用第一个选项开始。 如果您发现自己想要重用逻辑,您可以将函数从组件中分离出来。 如果您想要一个包含逻辑和标记的可重用部分,或者您想要隔离以在整个应用程序中使用,则第三个选项是最好的。
在这种情况下,我们将创建一个新组件,因为稍后我们将要添加更多数据,并且我们正在将标记与转换逻辑相结合。
新组件将被称为 AnimalDetails。 为此,请创建一个新目录:
mkdir src/components/AnimalDetails
接下来,在文本编辑器中打开 AnimalDetails.js:
nano src/components/AnimalDetails/AnimalDetails.js
在文件中,制作一个小组件,将 diet 显示为表情符号:
包装教程/src/components/AnimalDetails/AnimalDetails.js
import React from 'react';
import PropTypes from 'prop-types';
import './AnimalDetails.css';
function convertFood(food) {
switch(food) {
case 'insects':
return '🐜';
case 'meat':
return '🍖';
case 'plants':
default:
return '🌱';
}
}
export default function AnimalDetails({ diet }) {
return(
<div className="details">
<h4>Details:</h4>
<div>
Diet: {diet.map(food => convertFood(food)).join(' ')}
</div>
</div>
)
}
AnimalDetails.propTypes = {
diet: PropTypes.arrayOf(PropTypes.string).isRequired,
}
AnimalDetails.propTypes 对象设置函数以获取 diet 的属性,它是一个字符串数组。 然后在组件内部,代码循环 diet 并使用 switch 语句将字符串转换为表情符号。
保存并关闭文件。
您还导入了一些 CSS,所以现在让我们添加它。
打开AnimalDetails.css:
nano src/components/AnimalDetails/AnimalDetails.css
添加一些 CSS 为元素提供边框和边距,以将细节与组件的其余部分分开:
包装教程/src/components/AnimalDetails/AnimalDetails.css
.details {
border-top: gray solid 1px;
margin: 20px 0;
}
我们使用 .details 将规则与具有 details 的 className 的元素进行匹配。
保存并关闭文件。
现在您有了一个新的自定义组件,您可以将它添加到您的 AnimalCard 组件中。 打开AnimalCard.js:
nano src/components/AnimalCard/AnimalCard.js
将 diet.join 语句替换为新的 AnimalDetails 组件,并通过添加突出显示的行将 diet 作为道具传递:
包装教程/src/components/AnimalCard/AnimalCard.js
import React from 'react';
import PropTypes from 'prop-types';
import AnimalDetails from '../AnimalDetails/AnimalDetails';
export default function AnimalCard({ diet, name, size }) {
return(
<div>
<h3>{name}</h3>
<div>{size}kg</div>
<AnimalDetails
diet={diet}
/>
</div>
)
}
AnimalCard.propTypes = {
diet: PropTypes.arrayOf(PropTypes.string).isRequired,
name: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
}
保存文件,您将在浏览器中看到新的详细信息。
使用 ...props 通过组件传递细节
这些组件可以很好地协同工作,但在 AnimalCard 中效率稍低。 您正在从 props 参数中明确提取 diet ,但您没有使用数据。 相反,您将其传递给组件。 这并没有本质上的错误——事实上,在过多的沟通方面犯错通常会更好。 但是这样做会使代码更难维护。 每当你想将新数据传递给 AnimalDetails 时,你需要更新三个地方:App,你传递 props 的地方,AnimalDetails,它消耗 props,和 [X159X ],这是中间人。
更好的方法是在 AnimalCard 中收集任何未使用的道具,然后将它们直接传递给 AnimalDetails。 这使您有机会在不更改 AnimalCard 的情况下更改 AnimalDetails。 实际上,AnimalCard 不需要知道任何关于进入 AnimalDetails 的道具或 PropTypes 的信息。
为此,您将使用 对象休息运算符 。 该操作符收集解构过程中没有取出的所有项目,并将它们保存到一个新对象中。
这是一个简单的例子:
const dog = {
name: 'dog',
diet: ['meat']
}
const { name, ...props } = dog;
在这种情况下,变量 name 将为 'dog',变量 props 将为 { diet: ['meat']}。
到目前为止,您已经将所有 props 当作 HTML 属性来传递,但您也可以使用对象来发送 props。 要将对象用作道具,您需要使用扩展运算符 - ...props - 用花括号括起来。 这会将每个键值对更改为一个道具。
打开AnimalCard.js:
nano src/components/AnimalCard/AnimalCard.js
在内部,从解构对象中删除 diet,然后将其余的道具收集到一个名为 props 的变量中。 然后将这些道具直接传递给 AnimalDetails:
包装教程/src/components/AnimalCard/AnimalCard.js
import React from 'react';
import PropTypes from 'prop-types';
import AnimalDetails from '../AnimalDetails/AnimalDetails';
export default function AnimalCard({ name, size, ...props }) {
return(
<div>
<h3>{name}</h3>
<div>{size}kg</div>
<AnimalDetails
{...props}
/>
</div>
)
}
AnimalCard.propTypes = {
name: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
}
请注意,您可以删除 diet PropType,因为您没有在此组件中使用道具。
在这种情况下,您只需将一个道具传递给 AnimalDetails。 如果您有多个道具,则顺序很重要。 后面的 prop 会覆盖前面的 prop,所以如果你有想要优先的 prop,请确保它是最后一个。 如果您的 props 对象的属性也是命名值,这可能会导致一些混乱。
保存并关闭文件。 浏览器将刷新,一切看起来都一样:
要了解 ...props 对象如何增加灵活性,让我们通过 AnimalCard 组件将 scientificName 传递给 AnimalDetails。
首先,打开App.js:
nano src/components/App/App.js
然后将 scientificName 作为道具传递:
包装教程/src/components/App/App.js
import React from 'react';
import './App.css';
import animals from './data';
import AnimalCard from '../AnimalCard/AnimalCard';
function App() {
return (
<div className="wrapper">
{animals.map(animal =>
<AnimalCard
diet={animal.diet}
key={animal.name}
name={animal.name}
size={animal.size}
scientificName={animal.scientificName}
/>
)}
</div>
);
}
export default App;
保存并关闭文件。
跳过AnimalCard; 您无需在那里进行任何更改。 然后打开 AnimalDetails 这样你就可以使用新的道具了:
nano src/components/AnimalDetails/AnimalDetails.js
新道具将是一个字符串,您将添加到 details 列表以及声明 PropType 的行:
包装教程/src/components/AnimalDetails/AnimalDetails.js
import React from 'react';
...
export default function AnimalDetails({ diet, scientificName }) {
return(
<div className="details">
<h4>Details:</h4>
<div>
Scientific Name: {scientificName}.
</div>
<div>
Diet: {diet.map(food => convertFood(food)).join(' ')}
</div>
</div>
)
}
AnimalDetails.propTypes = {
diet: PropTypes.arrayOf(PropTypes.string).isRequired,
scientificName: PropTypes.string.isRequired,
}
保存并关闭文件。 当您这样做时,浏览器将刷新,您将看到新的详细信息,而 AnimalCard 组件没有任何更改:
在这一步中,您学习了如何创建灵活的父道具,这些道具可以获取未知道具并使用扩展运算符将它们传递到嵌套组件中。 这是一种常见的模式,它将为您提供创建具有重点职责的组件所需的灵活性。 在下一步中,您将使用内置的 children 属性创建可以将未知组件作为道具的组件。
第 3 步 — 使用 children 创建包装器组件
在这一步中,您将创建一个包装组件,它可以将一组未知的组件作为道具。 这将使您能够像标准 HTML 一样嵌套组件,并且它将为您提供一种创建可重用包装器的模式,使您可以制作各种需要通用设计但内部灵活的组件。
React 为您提供了一个名为 children 的内置 prop,它收集所有子组件。 使用它可以使创建包装器组件直观且可读。
首先,创建一个名为 Card 的新组件。 这将是一个包装器组件,用于为任何新的卡片组件创建标准样式。
创建一个新目录:
mkdir src/components/Card
然后在文本编辑器中打开 Card 组件:
nano src/components/Card/Card.js
创建一个将 children 和 title 作为 props 的组件,并通过添加以下代码将它们包装在 div 中:
包装教程/src/components/Card/Card.js
import React from 'react';
import PropTypes from 'prop-types';
import './Card.css';
export default function Card({ children, title }) {
return(
<div className="card">
<div className="card-details">
<h2>{title}</h2>
</div>
{children}
</div>
)
}
Card.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.element),
PropTypes.element.isRequired
]),
title: PropTypes.string.isRequired,
}
children 的 PropTypes 是新的。 children 属性可以是 JSX 元素或 JSX 元素数组。 title 是一个字符串。
保存并关闭文件。
接下来,添加一些样式。 打开Card.css:
nano src/components/Card/Card.css
您的卡片将在详细信息下方有一个边框和一条线。
包装教程/src/components/Card/Card.css
.card {
border: black solid 1px;
margin: 10px;
padding: 10px;
width: 200px;
}
.card-details {
border-bottom: gray solid 1px;
margin-bottom: 20px;
}
保存并关闭文件。 现在你有了你的组件,你需要使用它。 您可以在 App.js 中用 Card 组件包装每个 AnimalCard,但是由于名称 AnimalCard 暗示它已经是 Card,因此最好使用 AnimalCard 内部的 Card 组件。
打开AnimalCard:
nano src/components/AnimalCard/AnimalCard.js
与其他道具不同,您不会明确传递 children 。 相反,您包含 JSX,就好像它们是 HTML 子元素一样。 换句话说,您只需将它们嵌套在元素内,如下所示:
包装教程/src/components/AnimalCard/AnimalCard.js
import React from 'react';
import PropTypes from 'prop-types';
import Card from '../Card/Card';
import AnimalDetails from '../AnimalDetails/AnimalDetails';
export default function AnimalCard({ name, size, ...props }) {
return(
<Card title="Animal">
<h3>{name}</h3>
<div>{size}kg</div>
<AnimalDetails
{...props}
/>
</Card>
)
}
AnimalCard.propTypes = {
name: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
}
与 React 组件不同,您不需要将单个根元素作为子元素。 这就是为什么 Card 的 PropType 指定它可以是元素数组或单个元素的原因。 除了将 children 作为嵌套组件传递之外,您还为卡片提供了 Animal 的标题。
保存并关闭文件。 当您这样做时,浏览器将刷新,您将看到更新的卡片组件。
现在您有了一个可重用的 Card 组件,它可以采用任意数量的嵌套子级。 这样做的主要优点是您可以将 Card 与任意组件一起使用。 如果您想制作 Plant 卡,可以通过使用 Card 组件包装植物信息来实现。 它甚至根本不需要关联:如果您想在一个完全不同的应用程序中重用 Card 组件,该应用程序列出了音乐或帐户数据等内容,您也可以这样做。 Card 组件不关心孩子是什么; 您只是在重用包装器元素,在这种情况下是样式化的边框和标题。
使用 children 的缺点是您只能拥有一个子道具的实例。 有时,您会希望组件在多个位置具有自定义 JSX。 幸运的是,您可以通过将 JSX 和 React 组件作为道具传递来做到这一点,我们将在下一步中介绍。
第 4 步 — 将组件作为道具传递
在此步骤中,您将修改 Card 组件以将其他组件作为道具。 这将为您的组件提供最大的灵活性,以在整个页面的多个位置显示未知组件或 JSX。 与只能使用一次的 children 不同,您可以拥有与 props 一样多的组件,让您的包装器组件能够适应各种需求,同时保持标准的外观和结构。
在这一步结束时,您将拥有一个可以包装子组件并在卡片中显示其他组件的组件。 当您需要创建需要比简单字符串和整数更复杂的信息的组件时,此模式将为您提供灵活性。
让我们修改 Card 组件以采用名为 details 的任意 React 元素。
首先,打开Card组件:
nano src/components/Card/Card.js
接下来,添加一个名为 details 的新道具并将其放在 <h2> 元素下方:
包装教程/src/components/Card/Card.js
import React from 'react';
import PropTypes from 'prop-types';
import './Card.css';
export default function Card({ children, details, title }) {
return(
<div className="card">
<div className="card-details">
<h2>{title}</h2>
{details}
</div>
{children}
</div>
)
}
Card.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.element),
PropTypes.element.isRequired
]),
details: PropTypes.element,
title: PropTypes.string.isRequired,
}
Card.defaultProps = {
details: null,
}
此道具将与 children 具有相同的类型,但它应该是可选的。 要使其成为可选,请添加默认值 null。 在这种情况下,如果用户没有传递任何详细信息,该组件仍然有效并且不会显示任何额外内容。
保存并关闭文件。 页面将刷新,您将看到与以前相同的图像:
现在向 AnimalCard 添加一些细节。 首先,打开AnimalCard。
nano src/components/AnimalCard/AnimalCard.js
由于 Card 组件已经在使用 children,因此您需要将新的 JSX 组件作为道具传递。 由于这些都是哺乳动物,请将其添加到卡片中,但将其包装在 <em> 标签中以使其成为斜体。
包装教程/src/components/AnimalCard/AnimalCard.js
import React from 'react';
...
export default function AnimalCard({ name, size, ...props }) {
return(
<Card title="Animal" details={<em>Mammal</em>}>
<h3>{name}</h3>
<div>{size}kg</div>
<AnimalDetails
{...props}
/>
</Card>
)
}
...
保存文件。 当您这样做时,浏览器将刷新并且您会看到更新,包括短语 Mammal。
这个道具已经很强大了,因为它可以接受任何大小的 JSX。 在这个示例中,您只添加了一个元素,但您可以传递任意数量的 JSX。 它也不必是 JSX。 例如,如果你有一个复杂的标记,你不会想直接在 prop 中传递它; 这将很难阅读。 相反,您可以创建一个单独的组件,然后将该组件作为道具传递。
要在工作中看到这一点,请将 AnimalDetails 传递给 details 属性:
包装教程/src/components/AnimalCard/AnimalCard.js
import React from 'react';
...
export default function AnimalCard({ name, size, ...props }) {
return(
<Card
title="Animal"
details={
<AnimalDetails
{...props}
/>
}
>
<h3>{name}</h3>
<div>{size}kg</div>
</Card>
)
}
...
AnimalDetails 更复杂,有很多行标记。 如果直接将它添加到 details 中,则会大大增加 prop 并使其难以阅读。
保存并关闭文件。 当您这样做时,浏览器将刷新,详细信息将显示在卡片顶部。
现在您有了一个 Card 组件,它可以采用自定义 JSX 并将其放置在多个位置。 你不局限于一个道具; 您可以将元素传递给任意数量的道具。 这使您能够创建灵活的包装组件,从而使其他开发人员有机会自定义组件,同时保留其整体样式和功能。
将组件作为道具传递并不完美。 它有点难以阅读并且不像传递 children 那样清晰,但它们同样灵活,您可以在组件中使用任意数量的它们。 您应该首先使用 children,但如果这还不够,请不要犹豫退回到道具。
在这一步中,您学习了如何将 JSX 和 React 组件作为 props 传递给另一个组件。 这将使您的组件能够灵活地处理包装组件可能需要多个道具来处理 JSX 或组件的许多情况。
结论
您已经创建了多种包装组件,这些组件可以灵活地显示数据,同时保持可预测的外观和结构。 您创建了可以收集未知道具并将其传递给嵌套组件的组件。 您还使用了内置的 children 属性来创建可以处理任意数量的嵌套元素的包装器组件。 最后,您创建了一个可以将 JSX 或 React 组件作为道具的组件,以便您的包装器组件可以处理不同自定义的多个实例。
包装器组件使您能够适应未知环境,同时最大限度地提高代码重用和一致性。 此模式对于创建将在整个应用程序中重用的基本 UI 元素非常有用,包括:按钮、警报、模式、幻灯片放映等。 你会发现自己多次返回它。
如果您想查看更多 React 教程,请查看我们的 React 主题页面,或返回 如何在 React.js 系列页面中编码 。