Children¶
Внимание
Использование Children
является необычным и может привести к небезопасному коду.
Children
позволяет вам манипулировать и преобразовывать JSX, который вы получили в качестве свойства children
.
1 2 3 |
|
Описание¶
Children.count(children)
¶
Вызовите Children.count(children)
для подсчета количества детей в структуре данных children
.
1 2 3 4 5 6 7 8 9 10 |
|
Параметры
children
: Значение пропсаchildren
, полученного вашим компонентом.
Возвращает
Количество узлов внутри этих children
.
Ограничения
- Пустые узлы (
null
,undefined
и булевы), строки, числа и React elements считаются отдельными узлами. Массивы не считаются отдельными узлами, но их дочерние элементы считаются. Обход не идет глубже, чем React-элементы: они не отображаются, и их дочерние элементы не обходятся. Фрагменты не обходятся.
Children.forEach(children, fn, thisArg?)
¶
Вызовите Children.forEach(children, fn, thisArg?)
, чтобы выполнить некоторый код для каждого ребенка в структуре данных children
.
1 2 3 4 5 6 7 8 9 10 |
|
Параметры
children
: Значение свойстваchildren
, полученного вашим компонентом.fn
: Функция, которую вы хотите запускать для каждого ребенка, аналогично обратному вызову метода массиваforEach
. Она будет вызвана с дочерним элементом в качестве первого аргумента и его индексом в качестве второго аргумента. Индекс начинается с0
и увеличивается при каждом вызове.- опционально
thisArg
: Значениеthis
, с которым должна быть вызвана функцияfn
. Если опущено, тоundefined
.
Возвращает
Children.forEach
возвращает undefined
.
Ограничения
- Пустые узлы (
null
,undefined
и булевы), строки, числа и React elements считаются отдельными узлами. Массивы не считаются отдельными узлами, но их дочерние элементы считаются. Обход не идет глубже, чем React-элементы: они не отображаются, и их дочерние элементы не обходятся. Фрагменты не обходятся.
Children.map(children, fn, thisArg?)
¶
Вызовите Children.map(children, fn, thisArg?)
для отображения или преобразования каждого ребенка в структуре данных children
.
```js RowList.js active import { Children } from 'react';
function RowList({ children }) { return (
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Параметры
children
: Значение свойстваchildren
, полученного вашим компонентом.
Возвращает
Если children
является допустимым элементом, возвращает этот элемент.
В противном случае выбрасывает ошибку.
Ограничения
- Этот метод всегда отбрасывается, если вы передаете массив (например, возвращаемое значение
Children.map
) в качествеchildren
. Другими словами, он проверяет, чтоchildren
- это один элемент React, а не массив с одним элементом.
Children.toArray(children)
¶
Вызовите Children.toArray(children)
для создания массива из структуры данных children
.
1 2 3 4 5 6 7 |
|
Параметры
children
: Значение параметраchildren
, полученное вашим компонентом.
Возвращает
Возвращает плоский массив элементов в children
.
Ограничения
- Пустые узлы (
null
,undefined
и булевы) будут опущены в возвращаемом массиве. Ключи возвращаемых элементов будут вычислены из ключей исходных элементов и их уровня вложенности и позиции. Это гарантирует, что уплощение массива не приведет к изменениям в поведении.
Использование¶
Преобразование детей¶
Для преобразования дочерних JSX, которые ваш компонент получает как пропс children
, вызовите Children.map
:
1 2 3 4 5 6 7 8 9 10 11 |
|
В приведенном выше примере RowList
оборачивает каждого полученного дочернего элемента в контейнер <div className="Row">
. Допустим, родительский компонент передает три тега <p>
в качестве пропса children
в RowList
:
1 2 3 4 5 |
|
Затем, с вышеуказанной реализацией RowList
, окончательный результат рендеринга будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 |
|
Children.map
похож на на преобразование массивов с помощью map()
Разница в том, что структура данных children
считается очевидной. Это означает, что даже если иногда это массив, вы не должны предполагать, что это массив или любой другой конкретный тип данных. Вот почему вы должны использовать Children.map
, если вам нужно преобразовать его.
1 2 3 4 5 6 7 8 9 10 11 |
|
1 2 3 4 5 6 7 8 9 10 11 |
|
Почему пропс children
не всегда является массивом?
В React пропс children
считается прозрачной структурой данных. Это означает, что вы не должны полагаться на то, как он структурирован. Для преобразования, фильтрации или подсчета детей следует использовать методы Children
.
На практике структура данных children
часто представляется в виде внутреннего массива. Однако если есть только один ребенок, то React не будет создавать дополнительный массив, так как это приведет к ненужным затратам памяти. Пока вы используете методы Children
вместо прямого интроспекции пропса children
, ваш код не сломается, даже если React изменит способ реализации структуры данных.
Даже когда children
является массивом, Children.map
имеет полезное специальное поведение. Например, Children.map
объединяет keys на возвращаемых элементах с ключами на children
, которые вы ему передали. Это гарантирует, что оригинальные дочерние элементы JSX не "потеряют" ключи, даже если они будут обернуты, как в примере выше.
Структура данных children
не включает рендеринг вывода компонентов, которые вы передаете в виде JSX. В приведенном ниже примере children
, полученный RowList
, содержит только два элемента, а не три:
<p>Это первый элемент.</p>
.<MoreRows />
.
Вот почему в этом примере генерируются только две обертки строк:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
1 2 3 4 5 6 7 8 9 10 11 |
|
Невозможно получить рендерный вывод внутреннего компонента типа <MoreRows />
при манипулировании children
. Вот почему обычно лучше использовать одно из альтернативных решений.
Выполнение кода для каждого ребенка¶
Вызовите Children.forEach
для перебора каждого ребенка в структуре данных children
. Он не возвращает никакого значения и похож на метод array forEach
. Вы можете использовать его для выполнения пользовательской логики, например, для создания собственного массива.
1 2 3 4 5 6 7 8 9 10 11 |
|
1 2 3 4 5 6 7 8 9 10 11 |
|
Как упоминалось ранее, не существует способа получить рендерный вывод внутреннего компонента при манипулировании children
. Вот почему обычно лучше использовать одно из альтернативных решений.
Подсчет детей¶
Вызовите Children.count(children)
для подсчета количества детей.
1 2 3 4 5 6 7 8 9 10 11 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Как упоминалось ранее, не существует способа получить рендерный вывод внутреннего компонента при манипулировании children
. Вот почему обычно лучше использовать одно из альтернативных решений.
Преобразование детей в массив¶
Вызовите команду Children.toArray(children)
, чтобы превратить структуру данных children
в обычный массив JavaScript. Это позволит вам манипулировать массивом с помощью встроенных методов массивов, таких как filter
, sort
или reverse
.
1 2 3 4 5 6 7 8 9 10 11 |
|
1 2 3 4 5 6 7 |
|
Как упоминалось ранее, не существует способа получить рендерный вывод внутреннего компонента при манипулировании children
. Вот почему обычно лучше использовать одно из альтернативных решений.
Альтернативы¶
В этом разделе описаны альтернативы API Children
(с большой буквы C
), который импортируется следующим образом:
1 |
|
Не путайте это с использованием свойства children
(строчная буква c
), что хорошо и поощряется.
Раскрытие нескольких компонентов¶
Манипулирование дочерними компонентами с помощью методов Children
часто приводит к хрупкому коду. Когда вы передаете дочерние элементы компоненту в JSX, вы обычно не ожидаете, что компонент будет манипулировать или преобразовывать отдельные дочерние элементы.
Когда это возможно, старайтесь избегать использования методов Children
. Например, если вы хотите, чтобы каждый дочерний элемент RowList
был обернут в <div className="Row">
, экспортируйте компонент Row
и вручную оберните каждый ряд в него следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
1 2 3 4 5 6 7 |
|
В отличие от использования Children.map
, этот подход не оборачивает каждого ребенка автоматически. Однако, этот подход имеет значительное преимущество по сравнению с предыдущим примером с Children.map
, потому что он работает, даже если вы продолжаете извлекать больше компонентов. Например, он все еще работает, если вы извлекаете свой собственный компонент MoreRows
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
1 2 3 4 5 6 7 |
|
Это не будет работать с Children.map
, потому что он будет "видеть" <MoreRows />
как одного ребенка (и одну строку).
Принятие массива объектов в качестве пропса¶
Вы также можете явно передать массив в качестве пропса. Например, этот RowList
принимает в качестве пропса массив rows
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
1 2 3 4 5 6 7 8 9 10 11 |
|
Поскольку rows
является обычным массивом JavaScript, компонент RowList
может использовать для него встроенные методы массивов, такие как map
.
Этот паттерн особенно полезен, когда вы хотите иметь возможность передавать больше информации в виде структурированных данных вместе с дочерними элементами. В приведенном ниже примере компонент TabSwitcher
получает массив объектов в качестве пропса tabs
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
В отличие от передачи дочерних элементов как JSX, этот подход позволяет вам связать некоторые дополнительные данные, такие как header
, с каждым элементом. Поскольку вы работаете с tabs
напрямую, и это массив, вам не нужны методы Children
.
Вызов render prop для настройки рендеринга¶
Вместо того чтобы создавать JSX для каждого отдельного элемента, вы также можете передать функцию, которая возвращает JSX, и вызывать эту функцию, когда это необходимо. В этом примере компонент App
передает функцию renderContent
компоненту TabSwitcher
. Компонент TabSwitcher
вызывает renderContent
только для выбранной вкладки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Пропс типа renderContent
называется render prop, потому что это пропс, определяющий, как отобразить часть пользовательского интерфейса. Однако в нем нет ничего особенного: это обычный пропс, который, как оказалось, является функцией.
Пропсы Render являются функциями, поэтому вы можете передавать им информацию. Например, компонент RowList
передает id
и index
каждого ряда в пропс рендеринга renderRow
, который использует index
для выделения четных рядов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
Это еще один пример того, как родительские и дочерние компоненты могут сотрудничать, не манипулируя детьми.
Устранение неполадок¶
Я передаю пользовательский компонент, но методы Children
не показывают его результат рендеринга¶
Предположим, вы передаете два дочерних компонента в RowList
следующим образом:
1 2 3 4 |
|
Если вы выполните Children.count(children)
внутри RowList
, вы получите 2
. Даже если MoreRows
отобразит 10 различных элементов или вернет null
, Children.count(children)
все равно будет 2
. С точки зрения RowList
, он "видит" только JSX, который он получил. Он не "видит" внутренности компонента MoreRows
.
Это ограничение затрудняет извлечение компонента. Вот почему альтернативы предпочтительнее использования Children
.
Источник — https://react.dev/reference/react/Children