这是我的问题: 我需要使用材质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>
这使选择效果很好,使其看起来不错。但是我的问题是(显然)材料在select内部不接受React.Fragment(<></>
)。同时,我无法生成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>
如果我放置<div>
或其他元素而不是<></>
,则 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>
))}
])}
我被封锁了,我不知道如何解决。我想到了一个子组件,但是在渲染它时会遇到同样的问题。
有办法解决吗?
我留下了带有示例片段的链接:https://codesandbox.io/s/material-demo-h77i8
非常感谢您。
答案 0 :(得分:3)
您可以选中此代码框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数组,然后将其渲染到树中。
答案 1 :(得分:0)
我遍历了您的代码并对它进行了一些更改。您可以在这里找到它:https://codesandbox.io/s/material-demo-70svb
尽管它现在已经可以正常工作了,因为您必须使用useState定义状态然后保留您的值。我在eventHandlers中添加了控制台,以使您对可以解决问题的方法有一些了解
答案 2 :(得分:0)
我改变了三个方面:
menuItems
),然后渲染该数组(与Dipesh Dulal的答案相同)。Select
是否考虑要选择的菜单项保持同步。我已经将复选框包装到自定义的MenuItemWithCheckbox
组件中,该组件使用selected
属性(即injected by Select)来控制复选框的checked
状态。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>
);
}
答案 3 :(得分:-2)
我创建了一个与您的代码相似的示例。您可以在codeandbox 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>
);
}