【问题标题】:Submenu closes improperly in React + Material UI子菜单在 React + Material UI 中不正确关闭
【发布时间】:2019-10-10 17:42:17
【问题描述】:

我有一个嵌套的菜单结构,如下所示:

 menuItems = [
  {
    "key": "key1",
    "caption": "Text1"
  },
  {
    "key": "key2",
    "caption": "Text2",
    "subMenuItems": [
      {
        "key": "key3",
        "caption": "Text3",
        "subMenuItems": [
          {
            "key": "key4",
            "caption": "Text4"
          }
        ]
      },
      {
        "key": "key5",
        "caption": "Text5",
        "subMenuItems": []
      },
      {
        "key": "key6",
        "caption": "Text6"
      }
    ]
  },
  {
    "key": "key7",
    "caption": "Text7"
  }
]

我想在打开到其父级右侧的子菜单中打开子菜单项。类似于Angular Material Menu (Nested menu example)

我尝试使用 Material UI Menu,它会根据需要打开菜单 (See here),但存在以下 2 个问题:

  1. 如果您打开父菜单,然后再打开任何子菜单,您需要在外部单击菜单的次数与菜单相同。相反,它应该在点击外部时关闭所有父 + 子菜单。
  2. 如果您想通过单击另一个父菜单项来切换子菜单,您需要先单击父菜单(或按照问题 1,单击外部)关闭当前打开的子菜单,然后单击所需的父菜单项以打开相应的子菜单。

问题 1 可以使用 ClickAwayListenerimplemented here 来解决,但即使尝试切换到另一个子菜单(例如从“按钮 3”子菜单切换到“更多项目”),它也会关闭所有菜单。

谢谢。

【问题讨论】:

    标签: reactjs material-ui


    【解决方案1】:

    让我们构建一个抽屉/导航栏,它接收项目对象列表,然后以递归方式呈现。首先,像你在menuItems 中所做的那样定义结构,我会稍微改变一下:

    const items = [
         {name: 'Shallow Item', path='/', type:'shallow'},
         {
             name: 'Nested',
             type: 'nested',
             items:[
                 {name: 'Shallow again', path='/nestedpath'}
             ]
         }
    ]
    

    现在我们在您的列表中有两个项目,一个是一个普通的浅层项目,其中不包含嵌套选项,第二个是一个嵌套项目,其中包含一个浅层项目列表。

    现在我们必须构建或两个核心组件:<NestedItem><ShallowItem>。它们应该看起来像这样:

    const ShallowItem = ({item, onClick}) =>(
        <ListItem 
            component={Link} 
            to={item.path || #}
            onClick={onClick ? onClick : ()=>{} }
        >
            <ListItemText primary={item.name}/>
            {Boolean(nested) &&
                <Icon>
                    {collapsed ? 'arrow_drop_up' : 'arrow_drop_down'}
                </Icon>
            }
        </ListItem>
    )
    

    注意onClickto(react-router link) 属性的值是有条件的,因为我们将使用相同的组件来渲染第一层和NestedItem 的列表,比如这个:

    const NestedItem = ({ item }) => {
        const [collapsed, toggler] = useState(false)
        const classes = useStyles()
        return (
            <div>
                <ShallowItem
                    item={{ icon: item.icon, title: item.title }}
                    nested
                    collapsed={collapsed}
                    onClick={() => toggler(!collapsed)}
                />
                <Collapse in={collapsed}>
                    <List component='div'>
                        {item.items.map(item => (
                            <ShallowItem item={item} key={item.title}/>
                        ))}
                    </List>
                </Collapse>
    
            </div>
    
    )}
    

    我们走了,你抓住了这个想法吗?唯一真正被渲染的组件是ShallowItem,因为NestedItem 只是浅层项目的集合。所以我们首先渲染一个浅层项目,它打开一个包含更多浅层项目子列表的折叠容器。我们现在唯一需要做的就是检查项目是否有子项目,如果是,则呈现NestedItem,否则呈现ShallowItem。该函数是:

    const renderItems = items =>{
        for(let item of items){
            switch(item.type){
                case 'nested': return <NestedItem item={item} />
    
                case 'shallow' : return <ShallowItem item={item} />
            }
        }
    }
    

    现在您有了一种递归方式来渲染嵌套项,您只需要对一个组件进行样式化,并且现在使深度更深变得非常容易。我们在公司中使用此解决方案,效果非常好。

    【讨论】:

    • 这可能已经晚了 2 年,但我想知道您是否可以在解决方案的最后部分扩展更多内容?我不明白如何使它成为递归渲染。所示代码仅返回第一项并忽略其余部分。
    猜你喜欢
    • 1970-01-01
    • 2020-01-30
    • 1970-01-01
    • 2020-03-12
    • 2018-02-09
    • 1970-01-01
    • 2021-01-04
    • 2016-02-06
    • 2021-02-02
    相关资源
    最近更新 更多