React.Children.map рекурсивно?
Я создаю компонент React для отображения HTML-формы, и я нахожу необходимость рекурсивно перебирать все дочерние элементы моего родительского компонента Form, чтобы добавить дополнительные реквизиты только для дочерних компонентов определенного типа.
Пример (в JSX):
<Form>
<p>Personal Information</p>
<Input name="first_name" />
<Input name="last_name" />
<Input name="email" />
<Label>
Enter Your Birthday
<Input name="birthday" type="date" />
</Label>
</Form>
В этом примере я использую React.Children.map внутри моего компонента Form, а затем внутри функции map. Я проверяю дочерний тип "type" и "тип type.displayName" для определения того, с каким элементом я занимаюсь (либо собственный HTML-элемент, либо ReactElement):
var newChildren = React.Children.map(this.props.children, function(child) {
if (is.inArray(child.type.displayName, supportedInputTypes)) {
var extraChildProps = {
alertColor: this.props.alertColor,
displayErrors: this.state.displayErrors
}
return React.cloneElement(child, extraChildProps);
} else {
return child;
}
}.bind(this));
Моя проблема заключается в том, что React.Children.map неточно выполняет итерацию через this.props.children, и я бы хотел, чтобы он также проверял детей детей детей и т.д. Мне нужно добавить реквизиты только к моим компонентам ввода, чтобы они знали, когда для отображения ошибок и цвета, которые должны отображаться в сообщении об ошибке, и т.д. В приведенном выше примере вход дня рождения не получает необходимых реквизитов, поскольку он завернут в компонент Label.
Любой план React.Children.map иметь "рекурсивный" режим или любую другую утилиту, которая может выполнить то, что я пытаюсь сделать?
В конце дня я хотел бы написать одну функцию, которая отображает все дочерние (даже вложенные) для выполнения операции над ней (в этом случае клонирование).
Ответы
Ответ 1
Хотя это не испечено в React, это, безусловно, возможно:
import React from "react";
function recursiveMap(children, fn) {
return React.Children.map(children, child => {
if (!React.isValidElement(child)) {
return child;
}
if (child.props.children) {
child = React.cloneElement(child, {
children: recursiveMap(child.props.children, fn)
});
}
return fn(child);
});
}
Ответ 2
Если вы хотите подталкивать реквизиты в набор детей под своим компонентом "автоматически", вы также можете использовать контекст. Это позволяет вам "давать" свойства, функции и т.д. childContextTypes
компонентам, предоставляя им родительский childContextTypes
и getChildContext()
и предоставляя дочерние "запросы" им с contextTypes
.
Ответ 3
Короткий ответ на этот вопрос заключается в том, что в настоящее время это невозможно в отношении Реагирования 0.14. Как упоминал FakeRainBrigand, это, скорее всего, запах кода, так что это должно побуждать к переоценке вашей реализации.
Ответ 4
Я создал небольшую библиотеку для работы с детской структурой. Вы можете проверить это здесь:
https://github.com/fernandopasik/react-children-utilities
В вашем случае вы можете использовать метод deepMap:
import React from 'react';
import Children from 'react-children-utilities';
var newChildren = Children.deepMap(this.props.children, function(child) {
if (is.inArray(child.type.displayName, supportedInputTypes)) {
var extraChildProps = {
alertColor: this.props.alertColor,
displayErrors: this.state.displayErrors
}
return React.cloneElement(child, extraChildProps);
} else {
return child;
}
}.bind(this));
Ответ 5
Этот поток на самом деле пропускает правильный ответ:
const mapRecursive = (children, callback) => (
React.Children.map(
children,
child => (
child.props.children
? [callback(child), mapRecursive(child.props.children, callback)]
: callback(child)
),
)
);