Сводка: Можете ли вы "сгладить" фрагмент React, который передается как дочерний, перед выполнением функции React.Children.map в родительском компоненте? (Не уверен, что «flatten» - лучший термин, но, по сути, есть ли способ получить чистую абстракцию содержимого в React.Fragment, когда он передается как дочерний?)
У меня есть компонент элемента nav который автоматически отображает выпадающий список и добавляет уровень глубины всякий раз, когда компонент <NavItem/>
вложен в <NavItem/>.
Например:
<NavItem label="This is visible">
<NavItem label="This should be rendered within a dropdown"/>
<NavItem>
Кроме того, каждый <NavItem/>
будет увеличивать пропел уровня на 1 каждый время, когда ребенок вложен в себя. Код, прикрепленный в самом низу, работал отлично, и мой компонент отображал все очень хорошо, пока я не абстрагировал детей в свой собственный компонент. Например:
// sample 1
const CompanyNav = () => {
return (
<NavItem label="Some Nav Label (level 1)">
<NavItem label="Nav Item (level 2)" />
<NavItem label="Nav Item (level 2)" />
<NavItem label="Some Nav Label (level 2)">
<NavItem label="Nav Item (level 3)" />
<NavItem/>
<NavItem label="Nav Item (level 2)" />
<NavItem/>
)
}
// sample 2 (broken version)
const CompanyNavInner = () => {
return (
<React.Fragment>
<NavItem label="Nav Item (level 2)" />
<NavItem label="Nav Item (level 2)" />
<NavItem label="Some Nav Label (level 2)">
<NavItem label="Nav Item (level 3)" />
<NavItem/>
<NavItem label="Nav Item (level 2)" />
</React.Fragment>
)
}
const CompanyNav = () => {
return (
<NavItem label="Some Nav Label (level 1)">
<CompanyNavInner/>
<NavItem/>
)
}
Теперь я понимаю Я могу переместить свою функцию карты, но я хочу сохранить обе способности рендеринга. Другими словами, я знаю, что есть возможные способы заставить работать Sample2 ниже, но я хочу, чтобы, если это возможно, рендерил Sample 1 и 2.
// ------ COMPONENTS ------
// NavItem Component
import React, { Component } from "react"
import NavDropdown from "blueprint/Nav/NavDropdown/NavDropdown"
import { getClassNames } from "src/blueprint/utils"
class NavItem extends Component {
constructor(props) {
super(props)
this.state = {
isActive: false,
}
}
handleDropdown = () => {
this.setState({ isActive: !this.state.isActive })
}
render() {
const {
border,
className,
children,
href,
level,
label,
size,
...other
} = this.props
const classes = getClassNames([
"site-nav__item",
border ? "site-nav__item--bordered" : null,
children ? "site-nav__item--has-children" : null,
className,
])
return (
<li
className={classes}
data-dropdown-status={this.state.isActive ? "open" : null}
{...other}
>
<a
className="site-nav__item-link"
href={href ? href : "#"}
onClick={href ? null : e => e.preventDefault()}
>
{label}
</a>
{children && (
<NavDropdown
isActive={this.state.isActive}
level={parseFloat(level) + 1}
size={size}
>
{React.Children.map(children, child => {
return React.cloneElement(child, {
...child.props,
...{ level: parseFloat(level) + 1 },
})
})}
</NavDropdown>
)}
</li>
)
}
}
NavItem.defaultProps = {
level: 1,
}
export default NavItem
// Dropdown Component
import React from "react"
import { getClassNames } from "src/blueprint/utils"
const config = {
size: {
sm: "site-nav__dropdown--sm",
md: "site-nav__dropdown--md",
lg: "site-nav__dropdown--lg",
},
}
const NavDropdown = ({
className,
level,
size,
isActive,
children,
...props
}) => {
const classes = getClassNames([
"site-nav__dropdown",
`site-nav__level-${level}`,
config.size[size],
])
return (
<ul
data-nav-level={level}
className={isActive ? `active ${classes}` : classes}
{...props}
>
{children}
</ul>
)
}
export default NavDropdown
Просмотреть код песочницу здесь: https://codesandbox.io/s/intelligent-fog-3jpm0