Tutorial

Создание компонентов обертки в React с помощью свойств

Published on June 11, 2020
Русский
Создание компонентов обертки в React с помощью свойств

Автор выбрал Creative Commons для получения пожертвования в рамках программы Write for DOnations.

Введение

В этом обучающем модуле вы создадите компоненты обертки со свойствами с помощью библиотеки JavaScript React. Компоненты обертки — это компоненты, которые окружают неизвестные компоненты и предоставляют структуру по умолчанию для отображения дочерних компонентов. Эта схема полезна для создания элементов пользовательского интерфейса (UI), неоднократно используемых по всей конструкции, например типичных схем, шаблонов страниц и информационных плиток.

Для создания компонентов обертки сначала следует научиться использовать операторы rest и spread для сбора неиспользуемых свойств для передачи вложенным компонентам. Затем вы создадите компонент, который использует встроенный компонент children для обертки вложенных компонентов в JSX, как если бы они были элементами HTML. Наконец, вы передадите компоненты как свойства для создания гибких оберток, которые могут встроить настраиваемый JSX в разные места в компоненте.

В этом обучающем модуле вы создадите компоненты для отображения списка данных о животных в форме карточек. Вы научитесь разделять данные и компоненты реорганизации кода по мере создания гибких компонентов обертки. К концу этого обучающего модуля мы получим рабочее приложение, которое будет использовать расширенные методы свойств для создания многоразовых компонентов, которые будут масштабироваться и адаптироваться по мере развития и изменения приложения.

Примечание. В качестве первого шага необходимо настроить пустой проект, на основе которого вы будете выполнять упражнение из руководства. Если у вас уже есть рабочий проект и вы хотите напрямую работать со свойствами, начните выполнение руководства с шага 2.

Предварительные требования

Шаг 1 — Создание пустого проекта

На этом шаге мы создадим новый проект, используя Create React App. Затем вы удалите пример проекта и связанные файлы, которые устанавливаются при инициализации проекта. В заключение вы создадите простую структуру файлов для организации ваших компонентов. Это позволит получить надежную основу для создания приложения обертки этого обучающего руководства в следующем шаге.

Создайте новый проект. В командной строке запустите следующий скрипт для установки нового проекта с помощью create-react-app:

  1. npx create-react-app wrapper-tutorial

После завершения создания проекта перейдите в его директорию:

  1. cd wrapper-tutorial

В новой вкладке или окне терминала запустите проект, используя скрипт start Create React App​​​. Браузер автоматически обновит изменения, поэтому оставьте запущенным этот скрипт, пока вы работаете:

  1. npm start

Вы получите запущенный локальный сервер. Если проект не был открыт в браузере, вы можете открыть его, перейдя на страницу http://localhost:3000/. Если вы запустили приложение на удаленном сервере, воспользуйтесь адресом http://your_IP_address:3000.

Ваш браузер загрузит простое приложение React в качестве элемента Create React App:

Шаблон проекта React

Вы должны будете создать абсолютно новый набор компонентов, поэтому вам нужно начать с удаления определенного шаблонного кода, чтобы получить пустой проект.

Откройте src/App.js в текстовом редакторе. Это корневой компонент, который встраивается в страницу. Все компоненты будут запускаться отсюда. Дополнительную информацию об App.js можно найти в статье Настройка проекта React с помощью Create React App.

Откройте src/App.js с помощью следующей команды:

  1. nano src/App.js

Вы увидите следующий файл:

wrapper-tutorial/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, который должен возвращать набор пустых тегов: <></>​​​. В результате вы получите действительную страницу, которая ничего не возвращает. Окончательный код будет выглядеть следующим образом:

wrapper-tutorial/src/App.js

import React from 'react';
import './App.css';

function App() {
  return <></>;
}

export default App;

Сохраните изменения и закройте текстовый редактор.

В заключение удалите логотип. Вы не будете использовать ее в вашем приложении. Кроме того, вам нужно удалить неиспользуемые файлы, которые не пригодятся вам в работе. Это позволит вам избежать путаницы в перспективе.

В окне терминала введите следующую команду:

  1. rm src/logo.svg

Если вы откроете ваш браузер, то увидите пустой экран.

пустой экран в chrome

Теперь, когда вы успешно очистили пример проекта Create React App, создайте простую структуру файлов. Это позволит вам поддерживать ваши компоненты изолированными и независимыми.

Создайте каталог с именем components в каталоге src. В нем будут храниться все ваши пользовательские компоненты.

  1. mkdir src/components

Каждый компонент будет иметь собственный каталог для хранения файла компонента, а также стилей, изображений, если таковые имеются, и тестов.

Создайте каталог для App:

  1. mkdir src/components/App

Переместите все файлы App в этот каталог. Используйте подстановочный символ * для выбора любых файлов, начинающихся с App. вне зависимости от их расширения. Затем используйте команду mv для их отправки в новый каталог:

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

Далее обновите соответствующий путь импорта в index.js, который представляет собой корневой компонент и инициализирует весь процесс:

  1. nano src/index.js

Оператор импорта должен указывать на файл App.js в каталоге App, поэтому необходимо внести следующее изменение:

wrapper-tutorial/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

На этом шаге вы создадите компонент для отображения набора данных о группе животных. Ваш компонент будет содержать второй вложенный компонент для отображения определенной информации. Для подключения родительского и вложенного компонентов вы будете использовать операторы rest и spread для передачи неиспользуемых свойств от родительского компонента вложенному без необходимости знания родительским компонентом имен или типов свойств.

К концу этого шага у вас будет родительский компонент, который может предоставлять свойства вложенным компонентам без необходимости знать, что это за свойства. Это сохранит гибкость родительского компонента и позволит обновлять дочерний компонент без необходимости изменять родительский.

Создание компонента AnimalCard

Для начала создайте набор данных для ваших животных. Вначале откройте файл, содержащий набор данных в каталоге components/App:

  1. 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:

  1. mkdir src/components/AnimalCard

Откройте новый файл в каталоге:

  1. nano src/components/AnimalCard/AnimalCard.js

Теперь добавьте компонент, который возьмет name, diet и size в качестве свойства и отобразит:

wrapper-tutorial/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​​​. Данные diet указаны одной строкой с помощью метода join(). Каждый элемент данных включает соответствующий PropType для проверки правильности типа данных.

Сохраните и закройте файл.

Теперь, когда у вас есть компонент и данные, необходимо их объединить. Для этого импортируйте компонент и данные в корневой компонент вашего проекта: App.js.

Сначала откройте компонент:

  1. nano src/components/App/App.js

Теперь вы можете пройтись по данным и вернуть новый AnimalCard с соответствующими свойствами. Добавьте выделенные строки в App.js:

wrapper-tutorial/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 будет использовать для отслеживания сопоставленных данных. Наконец, вы обернете код с помощью div в className wrapper, который вы будете использовать для добавления определенного оформления.

Для добавления этого оформления откройте App.css:

  1. nano src/components/App/App.css

Удалите шаблонное оформление и добавьте гибкие свойства в класс, называемый wrapper​​​:

prop-tutorial/src/components/App/App.js
.wrapper {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    padding: 20px;
}

При этом будет использоваться формат flexbox для организации данных и выравнивания. padding дает немного места в окне браузера, а justify-content​​​ распределяет дополнительное пространство между элементами.

Сохраните и закройте файл. Когда вы сделаете это и обновите браузер, то увидите, что между данными теперь есть определенное свободное пространство.

Браузер с распределенными данными

Создание компонента деталей

Теперь у вас имеется простой компонент, отображающий данные. Но, допустим, вы хотели придать данным diet​​​​​ небольшую изюминку, конвертировав текст в эмодзи. Это можно сделать, конвертировав данные в вашем компоненте.

React​​ предусматривает гибкость. Поэтому, когда вы думаете о том, как конвертировать данные, у вас есть несколько вариантов:

  • Вы можете создать функцию внутри компонента, которая конвертирует текст в эмодзи.
  • Вы можете создать функцию и сохранить ее в файле вне компонента для возможности повторно использовать эту логику для различных компонентов.
  • Вы можете создать отдельный компонент, который конвертирует текст в эмодзи.

Каждый подход является нормальным, если применяется для соответствующего варианта использования. И вы будете менять их по мере создания приложения. Чтобы избежать преждевременных абстракций и сложностей, необходимо для начала использовать первый вариант. Если вы захотите повторно использовать логику, можно вытащить функцию отдельно из компонента. Третий вариант лучше всего подойдет, если вы хотите повторно использовать элемент, который включает логику и метку, или хотите изолировать использование в приложении.

В этом случае мы создадим новый компонент, поскольку мы хотим позже добавить еще данные и мы сочетаем метку с логикой конверсии.

Новый компонент будет называться AnimalDetails. Для его создания создайте новый каталог:

  1. mkdir src/components/AnimalDetails

Затем откройте AnimalDetails.js в текстовом редакторе:

  1. nano src/components/AnimalDetails/AnimalDetails.js

Внутри файла создайте небольшой компонент, в котором diet​​​​​​ отображается в виде эмодзи:

wrapper-tutorial/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:

  1. nano src/components/AnimalDetails/AnimalDetails.css

Добавьте несколько CSS, чтобы определить границу элемента и отделить детали от остальной части компонента:

wrapper-tutorial/src/components/AnimalDetails/AnimalDetails.css
.details {
    border-top: gray solid 1px;
    margin: 20px 0;
}

Мы используем .details для применения правила к элементам с className details.

Сохраните и закройте файл.

Теперь, когда у вас есть новый настраиваемый компонент, можно добавить ваш компонент AnimalCard. Откройте AnimalCard.js:

  1. nano src/components/AnimalCard/AnimalCard.js

Замените выражение diet.join на новый компонент AnimalDetails и передайте diet​​​ как свойство, добавив выделенные строки:

wrapper-tutorial/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 есть небольшая неэффективность. Вы явно извлекаете diet из аргумента props, но вы не используете данные, а передаете их компоненту. В этом нет ничего неправильного, по сути зачастую лучше ошибиться в сторону излишней коммуникации. Но при этом вы усложняете поддержку кода. Когда вы хотите передать новые данные AnimalDetails, необходимо обновить три места: App, где вы передаете свойство, AnimalDetails​​​, который принимает свойство, и AnimalCard​​​, который является посредником.

Лучше всего собрать неиспользуемые свойства внутри AnimalCard, а затем передать их напрямую AnimalDetails. Это дает вам возможность изменять AnimalDetails без изменения AnimalCard. Фактически AnimalCard не требуется ничего знать о свойствах или PropTypes, которые идут в AnimalDetails.

Для этого вы будете использовать оператор rest объекта. Этот оператор собирает все элементы, которые не были извлечены во время деструктурирования и сохраняет их в новый объект.

Вот простой пример:

const dog = {
    name: 'dog',
    diet: ['meat']
}

const { name, ...props  } = dog;

В этом случае переменная name будет 'dog', а переменная props будет { diet: ['meat']}​​.

До сих пор вы передавали все свойства, как если бы они были свойствами HTML, но вы также можете использовать объекты для отправки свойств. Чтобы использовать объект в качестве свойства, необходимо использовать оператор spread — ...props — в фигурных скобках. Это изменит каждую пару ключ-значение на свойство.

Откройте AnimalCard.js:

  1. nano src/components/AnimalCard/AnimalCard.js

Внутри удалите diet из деструктурированного объекта и соберите оставшиеся свойства в переменную под названием props. Затем передайте эти свойства напрямую AnimalDetails:

wrapper-tutorial/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,
}

Обратите внимание, что вы можете удалить PropType​​​ diet, поскольку вы не используете свойство в этом компоненте.

В этом случае вы только передаете одно свойство AnimalDetails​​​. В случаях, когда у вас есть несколько свойств, порядок будет иметь значение. Более позднее свойство перепишет более раннее, поэтому если какому-либо свойству вы хотите отдать приоритет, убедитесь, что оно является последним. Может возникнуть путаница, если ваш объект props имеет свойство, являющееся также названным значением.

Сохраните и закройте файл. Браузер обновится, и все будет выглядеть так же:

Браузер с деталями

Чтобы узнать, как объект ...props добавляет гибкость, давайте передадим scientificName в AnimalDetails через компонент AnimalCard.

Сначала откройте App.js:

  1. nano src/components/App/App.js

Затем передайте scientificName как свойство:

wrapper-tutorial/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 для возможности использовать новое свойство:

  1. nano src/components/AnimalDetails/AnimalDetails.js

Новое свойство будет строкой, которую вы добавите в список details​​​ вместе со строкой, указывающей PropType​​​:

wrapper-tutorial/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:

Браузер с научным названием

На этом шаге вы научились создавать гибкие родительские свойства, которые могут принимать неизвестные свойства передавать их во вложенные компоненты с помощью оператора spread. Это обычная схема, обеспечивающая гибкость создания компонентов с определенными обязанностями. В следующем шаге вы создадите компоненты, которые могут принимать неизвестные компоненты в качестве свойства с помощью встроенного свойства children.

Шаг 3 — Создание компонентов обертки с помощью children

На этом шаге вы создадите компонент обертки, который может принимать неизвестную группу компонентов в качестве свойства. Это позволит вам вкладывать такие компоненты, как стандартный HTML, и даст схему для создания многоразовых оберток, позволяющих создавать множество компонентов, которым нужен общий дизайн, но при этом гибкая внутренняя часть.

React дает встроенное свойство под названием children, которое собирает все дочерние компоненты. Это позволяет создавать интуитивные и читаемые компоненты обертки.

Для начала создайте новый компонент под названием Card. Это будет компонент обертки для создания стандартного стиля оформления для всех новых компонентов карточки.

Создайте новый каталог:

  1. mkdir src/components/Card

Затем откройте компонент Card​​​ в своем текстовом редакторе:

  1. nano src/components/Card/Card.js

Создайте компонент, который принимает children и title​​​ в качестве свойства и оборачивает их в div путем добавления следующего кода:

wrapper-tutorial/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,
}

PropTypes для children​​​ являются новыми. Свойство children​​ может быть ранее элементом JSX или массивом элементов JSX. title является строкой.

Сохраните и закройте файл.

Затем добавьте какое-то оформление. Откройте Card.css:

  1. nano src/components/Card/Card.css

Ваша карточка будет иметь границу и линию под деталями.

wrapper-tutorial/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;
}

Сохраните и закройте файл. Теперь, когда у вас есть компонент, необходимо его использовать. Вы можете обернуть каждый AnimalCard в компонент Card в App.js, но поскольку название AnimalCard​​​ ​​​подразумевает, что он уже Card, лучше использовать компонент Card​​​ внутри AnimalCard​​​.

Откройте AnimalCard:

  1. nano src/components/AnimalCard/AnimalCard.js

В отличие от других свойств вы не передаете children явно. Вместо этого вы включаете JSX, как если бы они были дочерними элементами HTML. Другими словами, вы только вкладываете их внутрь элемента, например так:

wrapper-tutorial/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 вам не нужен отдельный корневой элемент как дочерний. Поэтому PropType, указанный для Card​​​, может быть массивом элементов или одиночным элементом. Помимо передачи children​​​ как вложенных компонентов, вы даете карточке заголовок Animal.

Сохраните и закройте файл. Когда вы закроете его, браузер обновится, и вы увидите обновленный компонент карточки.

Браузер с карточками

Теперь у вас есть многоразовый компонент Card, который может принимать любое количество вложенных дочерних свойств. Основное преимущество этого заключается в том, что вы можете повторно использовать Card с любым произвольным компонентом. Если вы хотите сделать карту Plant, это можно сделать, обернув информацию о растении в компонент Card. Даже не требуется никаких соотношений: если вы хотите повторно использовать компонент Card в совершенно других приложениях, где указываются, например, музыкальные данные или данные учетной записи, это также возможно. Компоненту Card неважны дочерние компоненты, вы просто повторно используете элемент обертки, который в данном случае является границей и заголовком, выполненным в определенном стиле.

Минусом использования children является то, что вы сможете иметь только один экземпляр дочернего свойства. В отдельных случаях вам понадобится, чтобы компонент имел настраиваемый JSX в разных местах. К счастью, вы можете это сделать, передав компоненты JSX и React как свойства, которые мы рассмотрим в следующем шаге.

Шаг 4 — Передача компонентов как свойств

На этом шаге вы измените компонент Card для получения других компонентов как свойств. Это придаст вашему компоненту максимальную гибкость для отображения неизвестных компонентов или JSX в разных местах на странице. В отличие от children, который можно использовать только один раз, вы можете иметь столько компонентов, сколько есть свойств, обеспечивая для вашего компонента обертки способность адаптироваться к различным потребностям и в то же время поддерживать стандартный вид и структуру.

К концу этого шага у вас будет компонент, который сможет обернуть дочерние компоненты, а также отобразить другие компоненты в карточке. Эта структура обеспечит гибкость при создании компонентов, которым нужна информация сложнее, чем простые строки и целые числа.

Теперь изменим компонент Card для получения произвольного элемента React под названием details.

Сначала откройте компонент Card​​​:

  1. nano src/components/Card/Card.js

Затем добавьте новое свойство под названием details и разместите его под элементом <h2>:

wrapper-tutorial/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.

  1. nano src/components/AnimalCard/AnimalCard.js

Поскольку компонент Card уже использует children, вам нужно будет передать новый компонент JSX как свойство. Поскольку все эти животные являются млекопитающими, добавьте это в карточку, но оберните в теги <em>, чтобы выделить курсивом.

wrapper-tutorial/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. Если бы у вас была сложная метка, например, вы бы не хотели передавать ее напрямую в свойстве, это бы читалось бы с трудом. Вместо этого вы можете создать отдельный компонент и затем передать компонент как свойство.

Чтобы увидеть это в действии, передайте AnimalDetails для свойства details:

wrapper-tutorial/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, это бы существенно увеличило свойство и усложнило бы чтение.

Сохраните и закройте файл. После этого браузер обновится и в верхней части карточки появятся детали.

Карточка с деталями в верхней части

Теперь у вас есть компонент Card, который может принимать настраиваемый JSX и размещать его в разных точках. Вы не ограничены одним свойством, вы можете передавать элементы желаемому количеству свойств. Это дает вам возможность создавать гибкие компоненты обертки, которые могут предоставлять другим разработчикам возможность настраивать компонент, сохраняя общий стиль и функциональность.

Передача компонента как свойства не является идеальной. Они немного труднее читаются и не настолько понятны, как при передаче children, но они являются гибкими, и вы можете использовать их в компоненте в любом количестве. Сначала следует использовать children, но если этого недостаточно, смело возвращайтесь к свойствам.

На этом шаге вы научились передавать компоненты JSX и React как свойства другому компоненту. Это обеспечит гибкость вашего компонента для поведения в различных ситуациях, где компоненту обертки могут потребоваться различные свойства для обращения с JSX или компонентами.

Заключение

Вы создали различные компоненты обертки, которые могут гибко отображать данные и в то же время поддерживать прогнозируемый вид и структуру. Вы создали компоненты, которые могут собирать и передавать неизвестные свойства во вложенные компоненты. Также вы использовали встроенное свойство children для создания компонентов обертки, которое может обрабатывать произвольное количество вложенных элементов. Наконец, вы создали компонент, который может принимать компоненты JSX или React как свойство для того, чтобы ваш компонент обертки мог обрабатывать различные экземпляры разных конфигураций.

Компоненты обертки позволяют адаптироваться к неизвестным обстоятельствам, а также максимально увеличивают возможность повторного использования кода и обеспечивают его согласованность. Эта структура полезна для создания базовых элементов пользовательского интерфейса, которые вы будете использовать повторно по всему приложению, включая кнопки, предупреждения, типичные схемы, слайд-шоу и многое другое. Вы вернетесь к нему еще неоднократно.

Если вас интересуют другие обучающие модули по React, ознакомьтесь с нашей страницей тем по React или вернитесь на страницу серии Программирование на React.js.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors

Default avatar

Senior Technical Editor

Editor at DigitalOcean, fiction writer and podcaster elsewhere, always searching for the next good nautical pun!


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.