React - зацикливание на фрагментах
Резюме: Можете ли вы "сгладить" фрагмент React, который передается как дочерний, перед выполнением функции React.Children.map в родительском компоненте? (Не уверен, что "плоский" - лучший термин, но, по сути, есть способ получить чистую абстракцию содержимого в React.Fragment, когда он передается как дочерний элемент?)
У меня есть компонент элемента навигации, который автоматически отображает раскрывающийся список и добавляет уровень глубины всякий раз, когда <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, так и Sample 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