作者选择了Creative Commons作为Write for DOnations计划的一部分来接受捐赠。

简介

在本教程中,您将使用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 上安装它,请按照How to Install Node.js and Create a Local Development Environment on macOS或How To Install Node.js on 的 Installing Using a PPA 部分中的步骤操作Ubuntu 18.04。

  • 在本教程中,您将使用Create React App创建一个应用程序。您可以在How To Set Up a React Project with Create React App找到使用 Create React App 安装应用程序的说明以及有关其工作原理的一般信息。

  • 您将使用 React 组件,您可以在我们的如何在 React 中创建自定义组件教程中了解这些内容。它还有助于对 React 道具有一个基本的了解,您可以在如何使用道具自定义 React 组件中了解这一点。

  • 您还需要 JavaScript 的基本知识,您可以在我们的如何在 JavaScript 中编码系列中找到这些知识,以及 HTML 和 CSS 的基本知识。 HTML 和 CSS 的一个很好的资源是Mozilla Developer Network。

第一步——创建一个空项目

在这一步中,您将使用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 中:

React 模板项目

您将构建一组全新的自定义组件,因此您需要首先清除一些样板代码,以便您可以拥有一个空项目。

首先,在文本编辑器中打开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

如果您查看浏览器,您将看到一个空白屏幕。

chrome 中的空白屏幕

现在您已经清除了示例 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();

保存并退出文件。

现在项目已经建立,您可以创建您的第一个组件。

第二步——用...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'],
  }
]

此动物列表是数组的个对象,其中包括动物的名称、学名、体重和饮食。

保存并关闭文件。

接下来,为AnimalCard组件创建一个目录:

mkdir src/components/AnimalCard

在目录中打开一个新文件:

nano src/components/AnimalCard/AnimalCard.js

现在添加一个组件,它将namedietsize作为道具并显示它:

包装教程/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()方法](https://www.digitalocean.com/community/tutorials/how-to-use-array-methods-in-javascript-accessor-methods#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;

保存并关闭文件。

当您处理更复杂的项目时,您的数据将来自更多不同的地方,例如API、localStorage或静态文件。但是使用它们的过程将是相似的:将数据分配给一个变量并循环数据。在这种情况下,数据来自静态文件,因此您直接导入到变量中。

在此代码中,您使用.map() 方法迭代animals并显示道具。请注意,您不必使用每条数据。例如,您没有明确传递scientificName属性。您还将添加一个单独的key道具,React 将使用该道具来跟踪映射数据。最后,您将使用divclassNamewrapper包装代码,您将使用它来添加一些样式。

要添加此样式,请打开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将规则与classNamedetails的元素进行匹配。

保存并关闭文件。

现在您有了一个新的自定义组件,您可以将它添加到您的AnimalCard组件中。打开AnimalCard.js:

nano src/components/AnimalCard/AnimalCard.js

用新的AnimalDetails组件替换diet.join语句,并通过添加突出显示的行将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的效率有点低。您明确地将dietprops参数中提取出来,但您没有使用数据。相反,您将其传递给组件。这并没有本质上的错误——事实上,在过多的沟通方面犯错通常会更好。但是这样做会使代码更难维护。每当你想向AnimalDetails传递新数据时,你需要更新三个地方:App,传递 props 的地方,AnimalDetails,消耗 props 的地方,和AnimalCard,这是中间人。

更好的方法是在AnimalCard中收集任何未使用的道具,然后将它们直接传递给AnimalDetails。这使您有机会在不更改AnimalCard的情况下对AnimalDetails进行更改。实际上,AnimalCard不需要了解有关道具或PropTypes的任何信息将进入AnimalDetails

为此,您将使用对象休息运算符。该操作符收集解构过程中没有取出的所有项目,并将它们保存到一个新对象中。

这是一个简单的例子:

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

创建一个将childrentitle作为 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,
}

PropTypeschildren是新的。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 组件不同,您不需要将单个根元素作为子元素。这就是为什么PropTypeforCard指定它可以是元素数组或单个元素的原因。除了将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,但如果这还不够,请不要犹豫退回到 props。

在这一步中,您学习了如何将 JSX 和 React 组件作为 props 传递给另一个组件。这将使您的组件能够灵活地处理包装组件可能需要多个道具来处理 JSX 或组件的许多情况。

结论

您已经创建了多种包装组件,这些组件可以灵活地显示数据,同时保持可预测的外观和结构。您创建了可以收集未知道具并将其传递给嵌套组件的组件。您还使用了内置的children道具来创建可以处理任意数量的嵌套元素的包装器组件。最后,您创建了一个可以将 JSX 或 React 组件作为道具的组件,以便您的包装器组件可以处理不同自定义的多个实例。

包装器组件使您能够适应未知环境,同时最大限度地提高代码重用和一致性。此模式对于创建将在整个应用程序中重用的基本 UI 元素非常有用,包括:按钮、警报、模式、幻灯片放映等。你会发现自己多次回到它。

如果您想查看更多 React 教程,请查看我们的React 主题页面,或返回如何在 React.js 系列页面中编码。

Logo

React社区为您提供最前沿的新闻资讯和知识内容

更多推荐