如何使用道具定制 React 组件

_ 作者选择了 Creative Commons以作为 Write for Donations计划的一部分接收捐款。

介绍

在此教程中, 您可以通过将 _ props_ 传递到您的组件来创建自定义组件 。 道具是您向一个JSX元素提供的论点. 它们看起来像标准的HTML道具,但是它们不是预先定义的,可以有很多不同的[JavaScript data type (https://andsky.com/tech/tutorials/understanding-data-types-in-javascript),包括数字,字符串,功能,阵列,甚至其他反应组件. 您的自定义组件可以使用道具来显示数据或者使用数据来使组件交互. 道具是创建适应不同情况的组件的关键部分,了解道具会给你开发能处理独特情况的自定义组件的工具.

在向您的组件添加特性后,您将使用PropTypes来定义您期望组件接收的数据类型。PropTypes是一个简单的类型系统,用于检查数据是否在运行时匹配预期类型。

到教程结束时,您将使用各种标签来构建一个小型应用程序,该应用程序将包含一系列动物数据,并显示信息,包括名称,科学名称,大小,饮食和其他信息。

<$>[注] :第一步设置一个空的项目,你将建立的教程练习. 如果你已经有一个工作项目,并想直接进入与专业人员的工作,开始与 ** Step 2**

前提条件

  • 联合国 您需要运行一个开发环境 [Node.js] (https://nodejs.org/en/about/); 此教程在Node.js 版本10.20.1和npm 版本6.14.4. 上进行了测试. 要在 macOS 或 Ubuntu 18.04 上安装此功能,请遵循 [如何在 macOS (https://andsky.com/tech/tutorials/how-to-install-node-js-and-create-a-local-development-environment-on-macos 上安装节点并创建本地开发环境] 或 [如何在 Ubuntu 18.04 (https://andsky.com/tech/tutorials/how-to-install-node-js-on-ubuntu-18-04 上安装节点.js] 的 ** 部分使用 PPA
  • 联合国 在此教程之后, 您会使用 [创建回放 App (https://github.com/facebook/create-react-app ) 。 您可以在 [How To Set up a React Project with Create React App] (https://andsky.com/tech/tutorials/how-to-set-up-a-react-project-with-create-react-app] 上找到使用 Create React App 安装应用程序的指令. 此教程还假定了 React 组件的知识, 您可以在 our [How to Create Custom 组件 in React] (https://andsky.com/tech/tutorials/how-to-create-custom-components-in-react) 教程中学习此知识 。
  • 联合国 您还需要了解 JavaScript 的基本内容, 您可以在 [How To Code in JavaScript] (https://www.digitalocean.com/community/tutorial_series/how-to-code-in-javascript] 中找到这些内容, 以及 HTML 和 CSS 的基本知识 。 HTML和CSS的好资源是Mozilla开发者网络. .

第1步:创建一个空的项目

在此步骤中,您将使用 Create React App创建一个新的项目,然后您将删除在启动项目时安装的样本项目和相关文件。最后,您将创建一个简单的文件结构来组织您的组件。

在命令行中,运行以下脚本以使用Create-react-app安装新项目:

1npx create-react-app prop-tutorial

项目完成后,更改到目录:

1cd prop-tutorial

在一个新的终端卡或窗口中,使用 Create React App start script启动项目。浏览器将对更改进行自动更新,因此让此脚本在您工作的整个时间运行:

1npm start

如果项目未在浏览器窗口中打开,您可以通过导航到 http://localhost:3000/ 来打开它。 如果您正在从远程服务器运行,则地址将是 http://your_domain:3000

您的浏览器将加载一个简单的 React 应用程序,作为 Create React 应用程序的一部分:

React template project

您将建立一个全新的自定义组件组件,您将通过清除一些锅炉板代码开始,以便您可以有一个空的项目。

要开始,请在文本编辑器中打开 src/App.js。 这是注入到页面的根组件。 所有组件将从这里开始。 您可以在 [How To Set Up A React Project with Create React App] 找到有关 App.js 的更多信息(https://andsky.com/tech/tutorials/how-to-set-up-a-react-project-with-create-react-app# step-5-%E2%80%94-modifying-the-heading-tag-and-styling)。

使用以下命令打开src/App.js:

1nano src/App.js

你會看到這樣的檔案:

 1[label prop-tutorial/src/App.js]
 2import React from 'react';
 3import logo from './logo.svg';
 4import './App.css';
 5
 6function App() {
 7  return (
 8    <div className="App">
 9      <header className="App-header">
10        <img src={logo} className="App-logo" alt="logo" />
11        <p>
12          Edit <code>src/App.js</code> and save to reload.
13        </p>
14        <a
15          className="App-link"
16          href="https://reactjs.org"
17          target="_blank"
18          rel="noopener noreferrer"
19        >
20          Learn React
21        </a>
22      </header>
23    </div>
24  );
25}
26
27export default App;

删除./logo.svg导入标签行,然后更换返回声明中的所有内容,以返回一组空标签:<></>

 1[label prop-tutorial/src/App.js]
 2
 3import React from 'react';
 4import './App.css';
 5
 6function App() {
 7  return <></>;
 8}
 9
10export default App;

保存和退出文本编辑器。

最后,删除标志. 你不会在你的应用程序中使用它,你应该在工作时删除未使用的文件。

在终端窗口中,输入以下命令:

1rm src/logo.svg

如果你看你的浏览器,你会看到一个空白的屏幕。

blank screen in chrome

现在您已经清理了 Create React App 样本项目,创建一个简单的文件结构,这将有助于保持您的组件隔离和独立。

src目录中创建一个名为components的目录,此目录将包含您的所有自定义组件。

1mkdir src/components

每个组件将有自己的目录来存储组件文件,以及风格,如果有,图像和测试。

创建一个App目录:

1mkdir src/components/App

将所有应用文件移动到该目录中。 使用 wildcard,以选择任何开始于应用的文件,而不论文件的扩展。

1mv src/App.* src/components/App

最后,更新index.js中的相对导入路径,这是启动整个过程的根组件。

1nano src/index.js

导入声明需要指向应用程序目录中的App.js文件,所以请做出以下突出更改:

 1[label prop-tutorial/src/index.js]
 2import React from 'react';
 3import ReactDOM from 'react-dom';
 4import './index.css';
 5import App from './components/App/App';
 6import * as serviceWorker from './serviceWorker';
 7
 8ReactDOM.render(
 9  <React.StrictMode>
10    <App />
11  </React.StrictMode>,
12  document.getElementById('root')
13);
14
15// If you want your app to work offline and load faster, you can change
16// unregister() to register() below. Note this comes with some pitfalls.
17// Learn more about service workers: https://bit.ly/CRA-PWA
18serviceWorker.unregister();

保存和退出文件。

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

步骤 2 - 使用 Props 构建动态组件

在此步骤中,您将创建一个基于输入信息的变更的组件,称为 props. Props 是您传输给函数或类的参数,但由于您的组件被用 JSX 转换为 HTML 类型的对象,您将传输的 Props 就像它们是 HTML 属性一样。

在这里,您将创建一个组件,该组件将显示有关动物的信息。该组件将动物的名称和科学名称作为字符串,大小作为整数,饮食作为字符串的数组,并作为对象提供额外的信息。

到此步骤结束时,您将有一个自定义组件,该组件将消耗不同的附加值,您还将重复使用该组件以使用常见组件显示数据组件。

添加数据

首先,您需要一些样本数据,在名为数据的src/App目录中创建一个文件。

1touch src/components/App/data.js

在文本编辑器中打开新文件:

1nano src/components/App/data.js

接下来,添加一组 objects 您将使用作为样本数据:

 1[label prop-tutorial/src/components/App/data.js]
 2export default [
 3  {
 4    name: 'Lion',
 5    scientificName: 'Panthero leo',
 6    size: 140,
 7    diet: ['meat'],
 8  },
 9  {
10    name: 'Gorilla',
11    scientificName: 'Gorilla beringei',
12    size: 205,
13    diet: ['plants', 'insects'],
14    additional: {
15      notes: 'This is the eastern gorilla. There is also a western gorilla that is a different species.'
16    }
17  },
18  {
19    name: 'Zebra',
20    scientificName: 'Equus quagga',
21    size: 322,
22    diet: ['plants'],
23    additional: {
24      notes: 'There are three different species of zebra.',
25      link: 'https://en.wikipedia.org/wiki/Zebra'
26    }
27  }
28]

对象的数组包含各种数据,并会给你机会尝试各种特性. 每个对象都是一个单独的动物,有动物的名称,科学名称,大小,饮食,以及一个可选的字段称为附加,其中将包含链接或注释。

保存和退出文件。

创建组件

接下来,创建一个名为AnimalCard的站点组件,该组件最终会接收奖励并显示数据。

首先,在src/components中创建一个名为AnimalCard的目录,然后触摸一个名为src/components/AnimalCard/AnimalCard.js的文件和一个名为src/components/AnimalCard/AnimalCard.css的CSS文件。

1mkdir src/components/AnimalCard
2touch src/components/AnimalCard/AnimalCard.js
3touch src/components/AnimalCard/AnimalCard.css

在文本编辑器中打开AnimalCard.js:

1nano src/components/AnimalCard/AnimalCard.js

添加一个导入 CSS 并返回 <h2> 标签的基本组件。

1[label prop-tutorial/src/components/AnimalCard/AnimalCard.js]
2import React from 'react';
3import './AnimalCard.css'
4
5export default function AnimalCard() {
6  return <h2>Animal</h2>
7}

现在,您需要将数据和组件导入到您的基础应用组件中。

打开src/components/App/App.js:

1nano src/components/App/App.js

导入数据和组件,然后绕过数据,返回组件中的每个项目的组件:

 1[label prop-tutorial/src/components/App/App.js]
 2import React from 'react';
 3import data from './data';
 4import AnimalCard from '../AnimalCard/AnimalCard';
 5import './App.css';
 6
 7function App() {
 8  return (
 9    <div className="wrapper">
10      <h1>Animals</h1>
11      {data.map(animal => (
12        <AnimalCard key={animal.name}/>
13      ))}
14    </div>
15  )
16}
17
18export default App;

保存和退出文件. 在这里,您使用 .map() 数组方法 重复数据。 除了添加这个循环,您还有一个包装 div 与一类,你将使用的风格和一个 <h1> 标签来标记您的项目。

当您保存时,浏览器将重新加载,您将看到每个卡片的标签。

React project in the browser without styling

接下来,添加一些风格来排列项目,打开App.css:

1nano src/components/App/App.css

用以下内容替换内容以安排元素:

 1[label prop-tutorial/src/components/App/App.css]
 2.wrapper {
 3    display: flex;
 4    flex-wrap: wrap;
 5    justify-content: space-between;
 6    padding: 20px;
 7}
 8
 9.wrapper h1 {
10    text-align: center;
11    width: 100%;
12}

这将使用 flexbox来重新排列数据,这样它就会排列起来。

保存和退出文件. 当你这样做时,浏览器将更新,你会看到一些数据间隔。

React project in the browser with data spaced out

添加优惠

现在你已经设置了你的组件,你可以添加你的第一个支撑。当你绕过你的数据,你可以访问数据阵列中的每个对象和它所包含的项目. 你会添加每个数据到一个单独的支撑,然后你将使用你的动物卡组件。

打开App.js:

1nano src/components/App/App.js

名字添加到动物卡中。

 1[label prop-tutorial/src/components/App/App.js]
 2import React from 'react';
 3...
 4function App() {
 5  return (
 6    <div className="wrapper">
 7      <h1>Animals</h1>
 8      {data.map(animal => (
 9        <AnimalCard
10          key={animal.name}
11          name={animal.name}
12        />
13      ))}
14    </div>
15  )
16}
17
18export default App;

保存和退出文件. 该名称插件看起来像一个标准的HTML属性,但而不是一个字符串,你会通过动物对象的名称属性。

现在你已经向新组件传递了一个支持,你需要使用它。

1nano src/components/AnimalCard/AnimalCard.js

您传入组件的所有特许权被收集到一个对象中,该对象将是您函数的第一个参数。 Destructure 将对象用于提取个别特许权:

 1[label prop-tutorial/src/components/AnimalCard/AnimalCard.js]
 2
 3import React from 'react';
 4import './AnimalCard.css'
 5
 6export default function AnimalCard(props) {
 7  const { name } = props;
 8  return (
 9    <h2>{name}</h2>
10  );
11}

请注意,您不需要破坏一个支架来使用它,但这是本教程中处理样本数据的有用的方法。

在破坏对象后,您可以使用单个数据片段,在这种情况下,您将使用标签中的标题<h2>标签,用弯曲的框围绕该值,以便React能够将其评估为JavaScript。

例如,您可以创建一个类似于以下的 <h2> 元素: <h2>{props.title}</h2>. 破坏的优点是您可以收集未使用的 props,并使用 object rest operator

当你这样做时,浏览器将重新加载,你会看到每个动物的具体名称,而不是一个位置。

React projects with animal names rendered

名称属性是一个字符串,但代理可以是您可以传输到JavaScript函数的任何数据类型。

打开App.js文件:

1nano src/components/App/App.js

添加以下每一个字符串: scientificNamesizedietadditional. 这些字符串包括字符串、整数、数组和对象。

 1[label prop-tutorial/src/components/App/App.js]
 2import React from 'react';
 3...
 4
 5function App() {
 6  return (
 7    <div className="wrapper">
 8      <h1>Animals</h1>
 9      {albums.map(album => (
10        <AnimalCard
11          additional={animal.additional}
12          diet={animal.diet}
13          key={animal.name}
14          name={animal.name}
15          scientificName={animal.scientificName}
16          size={animal.size}
17        />
18      ))}
19    </div>
20  )
21}
22
23export default App;

由于您正在创建一个对象,您可以将其添加到您想要的任何顺序。 编写字符使您更轻松地扫描一个副本列表,特别是在较大的列表中。

保存并关闭文件. 打开AnimalCard.js

1nano src/components/AnimalCard/AnimalCard.js

这一次,破坏函数参数列表中的代理,并使用组件中的数据:

 1[label prop-tutorial/src/components/AnimalCard/AnimalCard.js]
 2import React from 'react';
 3import './AnimalCard.css'
 4
 5export default function AnimalCard({
 6  additional,
 7  diet,
 8  name,
 9  scientificName,
10  size
11}) {
12  return (
13    <div>
14      <h2>{name}</h2>
15      <h3>{scientificName}</h3>
16      <h4>{size}kg</h4>
17      <div>{diet.join(', ')}.</div>
18    </div>
19  );
20}

提取数据后,您可以将)`](https://andsky.com/tech/tutorials/how-to-use-array-methods-in-javascript-accessor-methods# join()),这将创建一个单独的列表。

当你这样做时,浏览器会更新,你会看到结构化数据。

React project with animals with full data

您可以使用额外对象创建类似的列表,但可以添加一个函数来通知用户数据,这将为您提供通过函数的机会,然后在调用函数时使用组件内部的数据。

打开App.js:

1nano src/components/App/App.js

创建一个名为显示额外数据的函数,将对象转换为字符串,并将其显示为警告。

 1[label prop-tutorial/src/components/App/App.js]
 2import React from 'react';
 3...
 4
 5function showAdditional(additional) {
 6  const alertInformation = Object.entries(additional)
 7    .map(information => `${information[0]}: ${information[1]}`)
 8    .join('\n');
 9  alert(alertInformation)
10};
11
12function App() {
13  return (
14    <div className="wrapper">
15      <h1>Animals</h1>
16      {data.map(animal => (
17        <AnimalCard
18          additional={animal.additional}
19          diet={animal.diet}
20          key={animal.name}
21          name={animal.name}
22          scientificName={animal.scientificName}
23          showAdditional={showAdditional}
24          size={animal.size}
25        />
26      ))}
27    </div>
28  )
29}
30
31export default App;

函数'showAdditional' 将对象转换为一组数组,其中第一个元素是密钥,第二个元素是值。

由于 JavaScript 可以接受函数作为参数,React 也可以接受函数作为附加函数,因此您可以将showAdditional转换为AnimalCard作为称为showAdditional的附加函数。

保存并关闭文件. 打开 AnimalCard:

1nano src/components/AnimalCard/AnimalCard.js

从 Props 对象中拉出 showAdditional' 函数,然后创建一个 <按钮> 与 onClick 事件呼叫该函数与 additional 对象:

 1[label prop-tutorial/src/components/AnimalCard/AnimalCard.js]
 2import React from 'react';
 3import './AnimalCard.css'
 4
 5export default function AnimalCard({
 6  additional,
 7  diet,
 8  name,
 9  scientificName,
10  showAdditional,
11  size
12}) {
13  return (
14    <div>
15      <h2>{name}</h2>
16      <h3>{scientificName}</h3>
17      <h4>{size}kg</h4>
18      <div>{diet.join(', ')}.</div>
19      <button onClick={() => showAdditional(additional)}>More Info</button>
20    </div>
21  );
22}

保存文件. 当你这样做时,浏览器将更新,你会看到一个按钮后每个卡片. 当你点击按钮,你会收到一个警告与额外的数据。

Alert with information

如果你试图点击狮子,你会收到一个错误,这是因为狮子没有额外的数据。

最后,在AnimalCard中添加动物包装器类名:

 1[label prop-tutorial/src/components/AnimalCard/AnimalCard.js]
 2import React from 'react';
 3import './AnimalCard.css'
 4
 5export default function AnimalCard({
 6...
 7  return (
 8    <div className="animal-wrapper">
 9...
10    </div>
11  )
12}

保存并关闭文件. 打开 AnimalCard.css:

1nano src/components/AnimalCard/AnimalCard.css

添加CSS以给卡片和按钮一个小边界和垫:

 1[label prop-tutorial/src/components/AnimalCard/AnimalCard.css]
 2.animal-wrapper {
 3    border: solid black 1px;
 4    margin: 10px;
 5    padding: 10px;
 6    width: 200px;
 7}
 8
 9.animal-wrapper button {
10    font-size: 1em;
11    border: solid black 1px;
12    padding: 10;
13    background: none;
14    cursor: pointer;
15    margin: 10px 0;
16}

此 CSS 将为卡片添加轻微的边界,并用边界和垫替换默认按钮样式。

保存和关闭文件. 当您这样做时,浏览器将更新,您将看到单个卡片中的数据。

React project with styled animal cards

在此时,您已经创建了两个自定义组件. 您已通过附件将数据从第一个组件传输到第二个组件. 附件包括各种数据,如字符串、整数、数组、对象和函数。

在下一步中,您将使用名为prop-types的类型系统来指定您的组件预计会看到的结构,从而在您的应用中创造可预测性并防止错误。

步骤 3 – 使用PropTypesdefaultProps创建可预测的配置文件

在此步骤中,您将使用PropTypes添加一个轻型系统到您的组件中。PropTypes像其他类型系统一样,通过明确定义您希望为某个 prop 接收的数据类型。它们还为您提供了在不需要 prop 时定义默认数据的机会。

到此步骤结束时,您将通过为每个 prop 定义类型来为自定义组件添加可预测性,从而确保下一个处理组件的人对组件所需的数据结构有明确的想法。

prop-types包包含在Create React App安装的一部分,所以要使用它,您只需要将其导入到您的组件中。

打开AnimalCard.js:

1nano src/components/AnimalCard/AnimalCard.js

然后从prop-types导入PropTypes:

1[label prop-tutorial/src/components/AnimalCard/AnimalCard.js]
2import React from 'react';
3import PropTypes from 'prop-types';
4import './AnimalCard.css'
5
6export default function AnimalCard({
7...
8}

在 JavaScript 中, 函数是对象,这意味着您可以使用点语法添加属性。

 1[label prop-tutorial/src/components/AnimalCard/AnimalCard.js]
 2import React from 'react';
 3import PropTypes from 'prop-types';
 4import './AnimalCard.css'
 5
 6export default function AnimalCard({
 7...
 8}
 9
10AnimalCard.propTypes = {
11  additional: PropTypes.shape({
12    link: PropTypes.string,
13    notes: PropTypes.string
14  }),
15  diet: PropTypes.arrayOf(PropTypes.string).isRequired,
16  name: PropTypes.string.isRequired,
17  scientificName: PropTypes.string.isRequired,
18  showAdditional: PropTypes.func.isRequired,
19  size: PropTypes.number.isRequired,
20}

保存并关闭文件。

正如你所看到的,有许多不同的 PropTypes. 这只是一个小样本;请参阅 官方的React文档 查看其他你可以使用的。

在这里,您指定名称必须是字符串scientificName属性是一样的。size是一个数字,它可以包括1.56等整数。

另一方面,饮食略有不同. 在这种情况下,你正在指定饮食将是一个数组,但你还需要指定这个数组将包含什么。在这种情况下,数组将只包含字符串。如果你想混合类型,你可以使用另一个名为OneOfType的标签,这取决于一个有效的PropTypes的数组。你可以使用oneOfType在任何地方,所以如果你想要大小成为一个数字或一个字符串,你可以改变它到这个:

1size: PropTypes.oneOfType([PropTypes.number, PropTypes.string])

在这种情况下,您正在指定一个对象,但要更清楚一点,您正在说明您希望该对象包含的内容。 要做到这一点,您使用PropTypes.shape,该对象需要一个附加字段,需要自己的PropTypes。 在这种情况下,链接注释都是PropTypes.string

目前,所有数据都很好的形成,并匹配了特许权. 如果PropTypes不匹配,请打开您的数据:

1nano src/components/App/data.js

将大小更改为第一个项目的字符串:

 1[label prop-tutorial/src/components/App/data.js]
 2export default [
 3  {
 4    name: 'Lion',
 5    scientificName: 'Panthero leo',
 6    size: '140',
 7    diet: ['meat'],
 8  },
 9...
10]

当您这样做时,浏览器将更新,您将在控制台中看到错误。

1[secondary_label Error]
2index.js:1 Warning: Failed prop type: Invalid prop `size` of type `string` supplied to `AnimalCard`, expected `number`.
3    in AnimalCard (at App.js:18)
4    in App (at src/index.js:9)
5    in StrictMode (at src/index.js:8)

Browser with type error

与其他类型的系统不同,如 TypeScript,‘PropTypes’在构建时不会给你发出警告,只要没有代码错误,它仍然会编译。

将数据更改为正确的类型:

 1[label prop-tutorial/src/components/App/data.js]
 2export default [
 3  {
 4    name: 'Lion',
 5    scientificName: 'Panthero leo',
 6    size: 140,
 7    diet: ['meat'],
 8  },
 9...
10]

保存并关闭文件。

打开AnimalCard.js:

1nano src/components/AnimalCard/AnimalCard.js

除了附加之外,每个 prop 都具有isRequired属性,这意味着它们是必需的. 如果您不包含所需的 prop,则代码仍会编译,但您将在控制台中看到运行时错误。

如果不需要 prop,您可以添加默认值,这是一个很好的做法,总是添加默认值,以防止运行时错误,如果不需要 prop. 例如,在AnimalCard组件中,你正在调用一个函数与额外数据。

若要防止此问题,请为附加添加一个默认Prop:

 1[label prop-tutorial/src/components/AnimalCard/AnimalCard.js]
 2import React from 'react';
 3import PropTypes from 'prop-types';
 4import './AnimalCard.css'
 5
 6export default function AnimalCard({
 7...
 8}
 9
10AnimalCard.propTypes = {
11  additional: PropTypes.shape({
12    link: PropTypes.string,
13    notes: PropTypes.string
14  }),
15...
16}
17
18AnimalCard.defaultProps = {
19  additional: {
20    notes: 'No Additional Information'
21  }
22}

您将DefaultProps添加到函数中使用点语法,就像您在propTypes中一样,然后添加一个默认值,如果 prop 是未定义的,则该组件应该使用。

保存和关闭文件. 当你这样做时,浏览器将更新. 更新后,点击 更多信息 按钮为** 狮子** . 它没有数据中的额外字段,所以标签是未定义的

Browser with default message in the alert

现在您的特许权已得到充分的记录,并且要么是需要的,要么是默认的,以确保可预测的代码。这将有助于未来的开发人员(包括您自己)了解组件需要的特许权。

结论

在本教程中,您已经创建了几个组件,这些组件使用代理来显示来自父母的信息。代理为您提供灵活性,以便开始将更大的组件分割成更小的,更集中的部件。

道具是构建复杂应用的关键工具,为创建能够适应所接收到的数据的组件提供了机会. 有了`PropTypes',你正在创造可预见和可读的组件,使一个团队能够重新利用彼此的工作,以建立一个灵活而稳定的代码基础。 如果您想查看更多 React 教程,请查看我们的[React Topic page (https://andsky.com/tags/react), 或者返回 [How To Code in React.js series page (https://www.digitalocean.com/community/tutorial_series/how-to-code-in-react-js).

Published At
Categories with 技术
comments powered by Disqus