【问题标题】:React How to render nested dropdown list in a form from a json data?React 如何从 json 数据中以表单呈现嵌套的下拉列表?
【发布时间】:2021-06-24 15:20:51
【问题描述】:

我有一个表单,我使用材料 ui 和 formik 来实现它。在里面,我需要一个带有嵌套选项的选择框,它最终应该看起来像这样。 (然后我应该得到选择的值并提交表单)

为简单起见:我只希望从 json 文件呈现递归子项:

我有一个嵌套有子对象的 json 文件。示例:

[
    {
        "label": "Bavullar ve \u00c7antalar",
        "value": 5181.0,
        "children": [
            {
                "label": "Al\u0131\u015fveri\u015f \u00c7antalar\u0131",
                "value": 5608.0
            },
            {
                "label": "Bavul Aksesuarlar\u0131",
                "value": 110.0,
                "children": [
                    {
                        "label": "Korumal\u0131 Kap D\u00fczenleyicileri ve B\u00f6lme Ekleri",
                        "value": 503014.0
                    },
                    {
                        "label": "Seyahat Keseleri",
                        "value": 5650.0
                    },
                    {
                        "label": "Seyahat \u015ei\u015fe ve Kaplar\u0131",
                        "value": 6919.0
                    }
                ]
            },
            {
                "label": "Bebek Bezi \u00c7antalar\u0131",
                "value": 549.0
            },
            {
                "label": "Bel \u00c7antalar\u0131",
                "value": 104.0
            },
            {
                "label": "Elbise \u00c7antalar\u0131",
                "value": 105.0
            }
...

这是我需要插入下拉列表的表单。

// import categories from json file

     import categories from '../gpc.tr.json'

 // Form component
<form onSubmit={formik.handleSubmit} className={classes.root} id='form'>
      <FormhelperText> <span style={{color:'red'}}>*</span> Set Google Product Category ID to </FormhelperText>
      <Select id="category" label="Set Google Product Category ID to" fullWidth variant="outlined" name="schema.category"
       value={formik.values.schema.category}
       onChange={formik.handleChange}
       error={formik.touched?.schema?.category && Boolean(formik.errors.category)}
       helpertext={formik.touched?.schema?.category && formik.errors.category}>
        // HERE I TRY TO INSERT A COMPONENT FOR DROPDOWN
        {renderCategories(categories)}
       </Select>

所以在 renderCategories 组件中,我使用递归来渲染嵌套的子级。但我无法让它工作。

const renderCategories = (categories) => {
  return (<div>
    {categories.map(i => {
      return <MenuItem key={i.value} value={i.value}>
        { i.label } 
       { i.children && renderCategories(i.children) } 
        </MenuItem>
    })}
    </div>
  )}

我收到&lt;li&gt; cannot appear as a descendant of &lt;li&gt;. 警告,所有子元素同时出现。就像图片中一样。即使我给它设置样式并隐藏子元素,我也觉得这里有问题,渲染它真的很慢。我应该如何解决这个问题?有没有更好的方法来实现它?

这里是沙盒链接:https://codesandbox.io/s/empty-moon-4diw4?file=/src/ProductForm.js

更新

我使用了“material-ui-nested-menu-item”包中的 NestedMenuItem。但是,我的递归函数仍然无法正常工作。

 function renderCategories (categories) {
     return categories.map((i) => {
        return(
        <NestedMenuItem key={i.value} value={i.value}
        label={i.label}
        parentMenuOpen={!!menuPosition}
        onClick={handleItemClick}>
               <MenuItem
              component={'div'}
              onClick={handleItemClick}
              key={i.value}> { i.label }
               </MenuItem>
         { i.children && renderCategories(i.children) } 
          </NestedMenuItem>
        )
      })
   }

我在这里迷路了,它没有正确渲染。你将如何在 react 的表单中实现递归下拉列表?

【问题讨论】:

标签: javascript json reactjs forms material-ui


【解决方案1】:

对于这么大的数据集,我会使用 ant design cascader。 https://ant.design/components/cascader/ 您可以使用不同的组件,并且不需要递归,只需将 json 作为选项传递即可。示例用法:

import { Cascader } from 'antd';

const options = [
  {
    value: 'zhejiang',
    label: 'Zhejiang',
    children: [
      {
        value: 'hangzhou',
        label: 'Hangzhou',
        children: [
          {
            value: 'xihu',
            label: 'West Lake',
          },
        ],
      },
    ],
  },
  {
    value: 'jiangsu',
    label: 'Jiangsu',
    children: [
      {
        value: 'nanjing',
        label: 'Nanjing',
        children: [
          {
            value: 'zhonghuamen',
            label: 'Zhong Hua Men',
          },
        ],
      },
    ],
  },
];

function onChange(value) {
  console.log(value);
}

ReactDOM.render(
  <Cascader options={options} onChange={onChange} placeholder="Please select" />,
  mountNode,
);

【讨论】:

    【解决方案2】:

    如果您将component="div" 传递给MenuItem 组件,您遇到的错误将被删除。 sandbox

    <MenuItem
                  onClick={() => console.log(123)}
                  component="div"
                  key={i.value}
                  value={i.value}
                >
    

    【讨论】:

    • 但是,它与渲染缓慢的问题无关。我现在正在考虑。刚刚发布它与您分享并检查它是否解决了您的问题
    • 好吧,沙箱中的代码不起作用,给出错误并且它不呈现选项。我不确定是什么导致了问题,请告诉我。
    • 抱歉。请再次检查链接
    • 现在它给出的结果和我的一样,我的意思是它应该像第一张图片一样呈现选项,所以用户可以选择。现在它一次渲染。
    • 是的,我说过。它刚刚删除了&lt;li&gt; cannot appear as a descendant of &lt;li&gt; 错误
    【解决方案3】:

    如果你想做嵌套列表,MenuItem 不是合适的组件。

    它使用 li 标记作为基础,这就是您收到此警告的原因。

    我建议您为此使用一个外部包,material-ui-nested-menu-item,正是为此创建的。

    您所要做的就是将MenuItem 替换为其默认的NestedMenuItem 组件,用Menu 容器包装它们并使用容器引用:

    <Menu
      open={!!menuPosition}
      onClose={() => setMenuPosition(null)}
      anchorReference="anchorPosition"
      anchorPosition={menuPosition}
    >
      <MenuItem onClick={handleItemClick}>Button 1</MenuItem>
      <MenuItem onClick={handleItemClick}>Button 2</MenuItem>
      <NestedMenuItem
        label="Button 3"
        parentMenuOpen={!!menuPosition}
        onClick={handleItemClick}
      >
        <MenuItem onClick={handleItemClick}>Sub-Button 1</MenuItem>
        <MenuItem onClick={handleItemClick}>Sub-Button 2</MenuItem>
        <NestedMenuItem
          label="Sub-Button 3"
          parentMenuOpen={!!menuPosition}
          onClick={handleItemClick}
        >
          <MenuItem onClick={handleItemClick}>Sub-Sub-Button 1</MenuItem>
          <MenuItem onClick={handleItemClick}>Sub-Sub-Button 2</MenuItem>
        </NestedMenuItem>
      </NestedMenuItem>
      <MenuItem onClick={handleItemClick}>Button 4</MenuItem>
      <NestedMenuItem
        label="Button 5"
        parentMenuOpen={!!menuPosition}
        onClick={handleItemClick}
      >
        <MenuItem onClick={handleItemClick}>Sub-Button 1</MenuItem>
        <MenuItem onClick={handleItemClick}>Sub-Button 2</MenuItem>
      </NestedMenuItem>
    </Menu>
    

    Click here 查看其用法的 CodeSandbox。

    另一种解决方案是通过提供component prop 使MenuItem 使用不同的标签作为基础。

    【讨论】:

    • @alisasani 让我们听听 OP 的意见,看看是否能解决问题。
    • 很抱歉打扰了您。我只是说问问能不能解决。
    • 我看到了这个包,但不幸的是,我也无法让它与我的 json 文件一起使用。在这里您提供了一个固定数据的示例,但是,我需要使用递归来查找所有子对象。
    • @bdemirka 继续使用我提供的递归,并根据您遇到的问题更新您的问题。
    猜你喜欢
    • 2021-03-18
    • 2018-08-08
    • 2020-07-02
    • 1970-01-01
    • 1970-01-01
    • 2014-10-12
    • 1970-01-01
    • 1970-01-01
    • 2021-06-19
    相关资源
    最近更新 更多