【问题标题】:React render siblings on Multiple Select Material UI在多选材质 UI 上反应渲染同级
【发布时间】:2020-06-03 05:13:19
【问题描述】:

这是我的问题: 我需要使用 Material UI 在 React 上创建一个多选(带复选框),如下图所示:

我有一个数组中的数据,如下所示

data = [
  {
    id: 199,
    name: "Argentina",
    state: [
      { name: "Buenos Aires", id: 1 },
      { name: "Cordoba", id: 2 },
      { name: "Chaco", id: 2 }
    ]
  },
  {
    id: 200,
    name: "USA",
    state: [
      { name: "California", id: 4 },
      { name: "Florida", id: 5 },
      { name: "Texas", id: 6 }
    ]
  }
]

完美。一切似乎都要做2个嵌套地图,没什么特别的。同时,我试试这个:

<InputLabel htmlFor="grouped-select">Grouping</InputLabel>
<Select
  defaultValue={[]}
  id="grouped-select"
  onChange={handleChange}
  multiple
>
  <MenuItem value="">
    <Checkbox />
    <em>All</em>
  </MenuItem>
  {data.map(country => (
    <>
      <ListSubheader key={country.id}>{country.name}</ListSubheader>
      {country.state.map(state => (
        <MenuItem key={state.id} value={state.id}>
          <Checkbox />
          {state.name}
        </MenuItem>
      ))}
    </>
  ))}
</Select>

这很好地呈现了选择,使它看起来不错。但我的问题是(显然)材料在选择中不接受 React.Fragment (&lt;&gt;&lt;/&gt;)。同时,我无法生成 Material UI 在文档中通过数组循环指定的结构:

<ListSubheader>Parent 1</ListSubHeader>
<MenuItem key={state.id} value={state.id}>Child1</MenuItem>
<MenuItem key={state.id} value={state.id}>Child2</MenuItem>
<MenuItem key={state.id} value={state.id}>Child...n</MenuItem>

<ListSubheader>Parent 2</ListSubHeader>
<MenuItem key={state.id} value={state.id}>Child</MenuItem>
<MenuItem key={state.id} value={state.id}>Child...n</MenuItem>

如果我使用&lt;div&gt; 或其他元素代替&lt;&gt;&lt;/&gt;onchange 事件将停止工作。

我也尝试过对数组使用渲染,但由于第二个兄弟不是 jsx,所以效果不佳,但我必须循环遍历数组。

{data.map(country => [
    <ListSubheader key={country.id}>{country.name}</ListSubheader>
    {country.state.map(state => (
      <MenuItem key={state.id} value={state.id}>
        <Checkbox />
        {state.name}
      </MenuItem>
    ))}
  ])}

我被卡住了,不知道怎么解决。我想到了一个子组件,但是在渲染它时我会遇到同样的问题。

有办法解决吗?

我留下了示例 sn-p 的链接:https://codesandbox.io/s/material-demo-h77i8

非常感谢。

【问题讨论】:

    标签: javascript reactjs material-ui


    【解决方案1】:

    您可以检查此代码框here

    您可以从这个答案中注意到,您始终可以在不需要片段的情况下创建元素。由于片段只是数组的包装器。因为,材料不希望其中有碎片。您可以使用纯数组来呈现您的元素,而不是将其包装成片段。

    
    function makeItems(data) {
      const items = [];
      for (let country of data) {
        items.push(<ListSubheader key={country.id}>{country.name}</ListSubheader>);
        for (let state of country.state) {
          items.push(
            <MenuItem key={state.id} value={state.id}>
              <Checkbox />
              {state.name}
            </MenuItem>
          );
        }
      }
      return items;
    }
    

    你可以看看我是如何使用另一个函数来推入 javascript 数组,然后将它们渲染到树中的。

    【讨论】:

    • 太棒了!它工作得很好。我不知道我以前怎么没想到,哈哈。谢谢!
    • 很高兴它有帮助?
    • 哇,终于成功了!我尝试了很多其他解决方案,这是解决我的You have provided an out-of-range value for the select component 错误同时仍然触发onChange 的唯一方法。谢谢!
    【解决方案2】:

    我浏览了您的代码并对其进行了一些更改。你可以在这里找到它:https://codesandbox.io/s/material-demo-70svb

    虽然它现在可以完全正常工作,因为您必须使用 useState 定义状态,然后保留您的值。我在 eventHandlers 中添加了控制台,让您了解可以采取哪些措施来解决您的问题

    【讨论】:

      【解决方案3】:

      我改变了三个不同的方面:

      • 与其使用片段,不如将菜单项添加到数组中(在下面的示例中为menuItems),然后渲染数组(与 Dipesh Dulal 的回答相同)。
      • 改进复选框的行为。在您的原始代码中,没有任何东西使复选框状态与Select 是否认为要选择菜单项保持同步。我已经将复选框包装到一个自定义的MenuItemWithCheckbox 组件中,该组件使用selected 属性(即injected by Select)来控制复选框的checked 状态。
      • 修复选定值的外观。所选值的默认显示(当不使用 Select 的 renderValue 属性时)使用 children of the menu item 作为显示文本。这意味着如果 MenuItem 的子项包含文本以外的内容,它将显示奇怪。通过引入自定义 MenuItemWithCheckbox 组件来处理复选框显示,直接子级现在只包含文本,并且默认显示可以正常工作。

      这是我修改后的沙箱版本的代码:

      import React from "react";
      import { makeStyles } from "@material-ui/core/styles";
      import InputLabel from "@material-ui/core/InputLabel";
      import MenuItem from "@material-ui/core/MenuItem";
      import ListSubheader from "@material-ui/core/ListSubheader";
      import FormControl from "@material-ui/core/FormControl";
      import Select from "@material-ui/core/Select";
      import { Checkbox } from "@material-ui/core";
      
      const useStyles = makeStyles(theme => ({
        formControl: {
          margin: theme.spacing(1),
          minWidth: 120
        }
      }));
      
      const data = [
        {
          id: 199,
          name: "Argentina",
          state: [
            { name: "Buenos Aires", id: 1 },
            { name: "Cordoba", id: 2 },
            { name: "Chaco", id: 3 }
          ]
        },
        {
          id: 200,
          name: "USA",
          state: [
            { name: "California", id: 4 },
            { name: "Florida", id: 5 },
            { name: "Texas", id: 6 }
          ]
        }
      ];
      
      const handleChange = ev => {
        //console.log(ev);
      };
      
      const MenuItemWithCheckbox = React.forwardRef(function MenuItemWithCheckbox(
        { children, selected, em = false, ...other },
        ref
      ) {
        return (
          <MenuItem {...other} selected={selected} ref={ref}>
            <Checkbox checked={selected} />
            {em && <em>{children}</em>}
            {!em && children}
          </MenuItem>
        );
      });
      
      export default function GroupedSelect() {
        const classes = useStyles();
        const menuItems = [];
        menuItems.push(
          <MenuItemWithCheckbox key="" value="" em={true}>
            All
          </MenuItemWithCheckbox>
        );
        data.forEach(country => {
          menuItems.push(
            <ListSubheader key={country.id}>{country.name}</ListSubheader>
          );
          country.state.forEach(state => {
            menuItems.push(
              <MenuItemWithCheckbox key={state.id} value={state.id}>
                {state.name}
              </MenuItemWithCheckbox>
            );
          });
        });
        return (
          <div>
            <FormControl className={classes.formControl}>
              <InputLabel htmlFor="grouped-select">Grouping</InputLabel>
              <Select
                defaultValue={[]}
                id="grouped-select"
                onChange={handleChange}
                multiple
              >
                {menuItems}
              </Select>
            </FormControl>
          </div>
        );
      }
      

      【讨论】:

        【解决方案4】:

        我创建了一个与您的代码类似的示例。您可以在代码框here 中尝试。希望对您有所帮助。
        这是我的代码:

        ...
        const names = [
          "Oliver Hansen",
          "Van Henry",
          "April Tucker",
          "Ralph Hubbard",
          "Omar Alexander",
          "Carlos Abbott",
          "Miriam Wagner",
          "Bradley Wilkerson",
          "Virginia Andrews",
          "Kelly Snyder"
        ];
        
        export default function MultipleSelect() {
          const classes = useStyles();
          const [personName, setPersonName] = React.useState([]);
        
          const handleChangeMultiple = event => {
            const { value } = event.target;
            const allIndex = value.indexOf("All");
            if (allIndex > -1) {
              const values = ["All"];
              for (let name of names) {
                values.push(name);
              }
              setPersonName(values);
            } else {
              setPersonName([]);
            }
          };
        
          return (
            <div>
              <FormControl className={classes.formControl}>
                <InputLabel id="demo-mutiple-checkbox-label">Tag</InputLabel>
                <Select
                  labelId="demo-mutiple-checkbox-label"
                  id="demo-mutiple-checkbox"
                  multiple
                  value={personName}
                  onChange={handleChangeMultiple}
                  input={<Input />}
                  renderValue={selected => selected.join(", ")}
                  MenuProps={MenuProps}
                >
                  <MenuItem key="All" value="All">
                    <Checkbox checked={personName.indexOf("All") > -1} />
                    <em>All</em>
                  </MenuItem>
                  {names.map(name => (
                    <MenuItem key={name} value={name}>
                      <Checkbox checked={personName.indexOf(name) > -1} />
                      <ListItemText primary={name} />
                    </MenuItem>
                  ))}
                </Select>
              </FormControl>
            </div>
          );
        }

        【讨论】:

        • 嗨,迈克尔。感谢您的回答,但这对我不起作用,因为我需要一个两级层次结构选择多个。我需要建立一个分组选择,像这样 (material-ui.com/es/components/selects/#grouping),但从一个真实的案例来看,要通过一个具有 2 级层次结构的数组。
        猜你喜欢
        • 2019-06-02
        • 2017-01-20
        • 2021-03-03
        • 2021-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-18
        • 2019-10-05
        • 1970-01-01
        相关资源
        最近更新 更多