2015-10-02 4 views
8

Я создаю компонент React для отображения HTML-формы, и я нахожу необходимость рекурсивно перебирать все дочерние элементы родительского компонента Form, чтобы добавить дополнительные реквизиты только для дочерних компонентов определенного типа.React.Children.map рекурсивно?

Пример (в 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 внутри моей форме компоненты, а затем внутри функции карты я проверяю «тип ребенка "и ребенка„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 только неглубоко перебирает это .props.children, и я хотел бы, чтобы он также проверял детей детей детей и т. д. Мне нужно добавить реквизиты только к моим компонентам ввода, чтобы они знали, когда показывать ошибки, и какой цвет выводить сообщение об ошибке, и т. Д. В приведенном выше примере вход дня рождения не получает необходимые реквизиты, потому что это завернутый в компонент Label.

Любой план для React.Children.map должен иметь «рекурсивный» режим или любую другую утилиту, которая может выполнить то, что я пытаюсь сделать?

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

+0

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

+1

Это проблема, которую вы решаете, избегая проблемы. Использование React.Children.map - это красный флаг, который вы делаете что-то сложное и запутанное. – FakeRainBrigand

+0

Согласовано. Я вижу, что использование компонентов более высокого порядка и/или mixins (временно) и/или что-то вроде глобального хранилища потоков - это способ предотвратить использование этого метода. В моем конкретном варианте использования это был Formsy.Миксин, который пришел мне на помощь, поскольку я смог определить, когда «displayErrors» просто вызвать функцию Formsy mixin isFormSubmitted(), которая решила мою проблему без необходимости клонирования детей с дополнительными реквизитами. – eriklharper

ответ

0

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

+0

Это возможно, он просто недоступен как часть React 0.14 Top-Level API. –

2

Если вы хотите надавить реквизиты на набор детей под своим компонентом «автоматически», вы также можете использовать context. Это позволяет вам «давать» свойства, функции и т. Д. Дочерним компонентам, предоставляя их от родителя childContextTypes и getChildContext() и имея дочерний «запрос» их с contextTypes.

+0

контекст отличный и работает на многое. Тем не менее, он работает только, если дети написаны для его поддержки. С другой стороны, рекурсивное клонирование позволяет вам вводить свойства ** любым ** компонентам, даже тем, которые не используют контекст. Мой любимый случай использования: инъекции 'value' и' onChange' реквизита (бесстыдная плагин: https://github.com/jedwards1211/react-bind-data) – Andy

9

Пока не запеченный в 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); 
    }); 
} 
+1

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

+0

Спасибо, что указали, что Оуэн. Исправлена! –

1

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

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)); 
1

Эта нить фактически не попадает в правильный ответ:

const mapRecursive = (children, callback) => (
    React.Children.map(
    children, 
    child => (
     child.props.children 
     ? [callback(child), mapRecursive(child.props.children, callback)] 
     : callback(child) 
    ), 
) 
); 
Смежные вопросы