Реакция - зацикливание на фрагменты - PullRequest
0 голосов
/ 18 марта 2020

Сводка: Можете ли вы "сгладить" фрагмент 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

...