React.Children.map()
отсутствует хороший пример , я не понимаю, как его набрать:
/*
<CheckboxGroup ...>
<Checkbox ... />
<Checkbox ... />
<Checkbox ... />
</CheckboxGroup>
*/
// Child
class Checkbox extends React.Component<CheckboxProps, void> {
// ...
}
// Parent
class CheckboxGroup extends React.Component<CheckboxGroupProps, void> {
// ...
render() {
const checkboxes = React.Children.map(this.props.children, checkbox =>
React.cloneElement(checkbox, {
name: this.props.name,
checked: this.props.checkedValues.includes(checkbox.props.value),
onChange: this.handleCheckboxChange.bind(this)
})
);
return (
<div>
{checkboxes}
</div>
);
}
}
(очень распространенный пример, см. http://stackoverflow.com/a/32371612/990356)
this.props.children
?React.cloneElement()
?React.Children.map()
?В большинстве случаев я получаю следующую ошибку: Type 'string' is not assignable to type 'ReactElement<any>'
, см. # 8131
отправьте запрос на перенос. Я пересмотрю это.
@vvakame Я не могу, я не понимаю, как это должно работать
я тоже. пожалуйста, поговорите с авторами определений.
Пинг @vsiao @thasner @basarat @ pspeter3 @beckend
Пинг / список авторов на основе:
вам нужно будет явно добавить аннотацию типа или утверждение типа checkbox
потому что нет гарантии, что this.props.children
не содержит значений, отличных от ReactElement. Например:
React.Children.map(this.props.children, (checkbox: React.ReactElement<CheckboxProps>) =>
React.cloneElement(checkbox) // should be ok
);
@vsiao спасибо большое, работает!
Вопрос:
const checkboxes = React.Children.map(this.props.children, checkbox =>
React.cloneElement(checkbox, {
name: this.props.name,
checked: this.props.checkedValues.includes(checkbox.props.value),
onChange: this.handleCheckboxChange.bind(this)
})
);
Решение:
const checkboxes = React.Children.map(props.children, (checkbox: React.ReactElement<CheckboxPropsInternal>) =>
React.cloneElement(checkbox, {
checked: props.checkedValues.includes(checkbox.props.value),
onChange: handleCheckboxChange
})
);
@tkrotoff, вы также можете сделать это:
React.Children.map(this.props.children, (child: number) => {
return child + 1
})
Это и предлагаемое решение просто используют тот факт, что React.Children.map
не является строго типизированным. С таким же успехом вы могли использовать any
вместо типа Checkbox
поскольку нет гарантии, что это будет тип потомков. Это означает, что у вас, скорее всего, возникнут ошибки времени выполнения, если кто-то не воспользуется компонентом, передав строку.
Как насчет этого:
class Slot extends React.PureComponent {
render () {
return this.props.children
}
}
const isReactElement = (obj: {}): obj is React.ReactElement<{}> => {
return obj.hasOwnProperty('type')
}
const isSlot = (obj: {}): obj is Slot => {
return isReactElement(obj) && obj.type === Slot
}
const getSlots = (children: React.ReactNode) => React.Children.map(children, (child: React.ReactChild): JSX.Element | null => {
if (isReactElement(child) && isSlot(child)) {
return child
}
return null
})
class ComponentWithSlots extends React.PureComponent {
render () {
const [header, footer] = getSlots(this.props.children)
return (
<div>
{header}
<div>
<h1>Welcome</h1>
<p>This is my lovely component with slots</p>
</div>
{footer}
</div>
)
}
}
class MyComponent extends React.PureComponent {
render () {
return (
<ComponentWithSlots>
<Slot>My Header!</Slot>
<Slot>
<div>github: @variousauthors</div>
</Slot>
</ComponentWithSlots>
)
}
}
Что будет отображать:
My Header!
Welcome
This is my lovely component with slots
github: <strong i="17">@variousauthors</strong>
Этот подход использует информацию о типе, которую мы _do_ имеем, а именно, что ReactChild
может быть ReactElement
(я использовал type
для обнаружения этого, но вы могли поосторожнее, если хотите). Мы также можем быть более наглядными и детально проработать Slot
. Я видел, как библиотека пользовательского интерфейса реализует шаблон, в котором классы имеют свои собственные слоты. Что-то вроде:
<Dropdown>
<Dropdown.Header>
<FancyIcon>My Header!</FancyIcon>
</Dropdown.Header>
{dropdownItems}
</Dropdown>
Таким образом, пользователь понимает значение каждого слота. Остальные дочерние элементы (не проанализированные как слоты) остаются в коллекции с именем children
и отображаются в виде корзины, что позволяет пользователю по-прежнему использовать children
обычном режиме, сохраняя при этом особое поведение для пользовательские слоты.
РЕДАКТИРОВАТЬ: React, по-видимому, имеет метод React.isValidElement
который вы можете использовать вместо моего пользовательского isReactElement
.
РЕДАКТИРОВАТЬ: вы можете еще что-то сделать, это ввести свойство children
в свой компонент, чтобы пользователю было разрешено передавать флажки только в качестве дочерних элементов. Это поможет избежать этих ошибок времени выполнения.
Самый полезный комментарий
Вопрос:
Решение: