React.Children.map()
está faltando um bom exemplo , não entendo como digitá-lo:
/*
<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>
);
}
}
(exemplo muito comum, consulte http://stackoverflow.com/a/32371612/990356)
this.props.children
?React.cloneElement()
?React.Children.map()
?Na maioria das vezes, acabo com o seguinte erro: Type 'string' is not assignable to type 'ReactElement<any>'
, consulte # 8131
envie uma solicitação de pull. Eu vou revisar isso.
@vvakame não posso, não entendo como deve funcionar
eu também. por favor, converse com os autores das definições.
Ping @vsiao @thasner @basarat @ pspeter3 @beckend
Lista de ping / autores com base em:
você precisará anotar ou declarar checkbox
explicitamente porque não há garantia de que this.props.children
não contenha valores não ReactElement. por exemplo:
React.Children.map(this.props.children, (checkbox: React.ReactElement<CheckboxProps>) =>
React.cloneElement(checkbox) // should be ok
);
@vsiao muito
Pergunta:
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)
})
);
Solução:
const checkboxes = React.Children.map(props.children, (checkbox: React.ReactElement<CheckboxPropsInternal>) =>
React.cloneElement(checkbox, {
checked: props.checkedValues.includes(checkbox.props.value),
onChange: handleCheckboxChange
})
);
@tkrotoff você também pode fazer isso:
React.Children.map(this.props.children, (child: number) => {
return child + 1
})
Esta e a solução sugerida estão apenas aproveitando o fato de que React.Children.map
não é fortemente tipado. Você também pode ter usado any
vez do tipo Checkbox
pois não há garantia de que esse será o tipo dos filhos. Isso significa que você provavelmente terá erros de tempo de execução se alguém usar incorretamente o componente passando uma string.
Que tal agora:
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>
)
}
}
Que irá renderizar:
My Header!
Welcome
This is my lovely component with slots
github: <strong i="17">@variousauthors</strong>
Esta abordagem tira proveito da informação de tipo que _temos_, que é que ReactChild
pode ser ReactElement
(eu usei type
para detectar isso, mas você poderia ser mais cuidado se você quiser). Também podemos ser mais descritivos e elaborados que Slot
. Eu vi uma biblioteca de IU implementar um padrão em que as classes tinham seus próprios slots anexados. Algo como:
<Dropdown>
<Dropdown.Header>
<FancyIcon>My Header!</FancyIcon>
</Dropdown.Header>
{dropdownItems}
</Dropdown>
Desta forma, o usuário tem clareza sobre o significado de cada slot. O resto dos filhos (aqueles não analisados como slots) são deixados em uma coleção chamada children
e renderizados à la carte, permitindo que o usuário ainda use children
normalmente, reservando um comportamento especial para o slots personalizados.
EDIT: React aparentemente tem um método React.isValidElement
que você pode usar no lugar do meu isReactElement
.
EDIT: Outra coisa que você pode fazer é digitar a propriedade children
em seu componente, de forma que um usuário só tenha permissão para passar as caixas de seleção como filhos. Isso ajudará a evitar esses erros de tempo de execução.
Comentários muito úteis
Pergunta:
Solução: