使 Header 每个组只出现一次
Make the Header Appear only once For Each Group
我正在尝试复制像 this. 这样的下拉菜单。
这是我的代码:
const data = [
{
id: 1,
header:1,
value:1
},
{
id: 2,
header:2,
value:2
},
{
id: 3,
header:2,
value:3
},
{
id: 4,
header:1,
value:4
}
];
useEffect(() => {
setData(data)
}[])
const renderSelectGroup = wfItem => {
const items =
<MenuItem key={wfItem.value} value={wfItem.value} >
{wfItem.value}
</MenuItem>
return [<ListSubheader>{wfItem.header}</ListSubheader>, items];
// return [items]
};
return (
<Col className="col-lg-3 col-sm-3 col-xs-3 col-md-3 mr-auto mb-2">
<FormControl variant="outlined" size="small" fullWidth>
<InputLabel>Dropdown With Grouping</InputLabel>
<MuiSelect
label="Dropdown with Grouping"
value={selectValuesWM}
onChange={handleWMChange}
>
{data.map(p => renderSelectGroup(p))}
</MuiSelect>
</FormControl>
</Col>
)
所以渲染后看起来像这样:
Header 1
Value 1
Header 2
Value 2
Header 2
Value 3
Header 1
Value 4
什么时候应该是这样的:
Header 1
Value 1
Value 4
Header 2
Value 2
Value 3
数据没有嵌套,所以我不确定我应该在这里采取哪些步骤来获得预期的结果。非常感谢任何帮助。
最好准确获取您需要的数据,然后相应地显示 UI。收到数据后,您可以使用找到的以下函数按 header 对数据进行分组 here:
let filteredData = [];
_data.forEach((r) => {
if (!filteredData[r.header]) filteredData[r.header] = [];
filteredData[r.header].push(r);
});
const groupedData = filteredData.filter(() => true);
这将为您提供以下数据:
const groupedData = [
[{ id: 1, header: 1, value: 1}, { id: 4, header: 1, value: 4}]
[{ id: 2, header: 2, value: 2}, { id: 3, header: 2, value: 3}]
];
从概念上讲,您可以这样渲染它:
{groupedData.map((groups) => (
<>
<h2>Header {groups[0].header}</h2>
{groups.map(({ id, value }) => (
<p>
id: {id} value: {value}
</p>
))}
</>
))}
为了匹配您的方法,您的 SelectGroup
组件可以如下所示:
const SelectGroup = ({ header, groups }) => (
<>
<ListSubheader>Header: {header}</ListSubheader>
{groups.map(({ id, value }) => (
<MenuItem key={id} value={value}>
id: {id} value: {value}
</MenuItem>
))}
</>
);
// and use it as such:
<MuiSelect>
{groupedData.map((groups) => (
<SelectGroup header={groups[0].header} groups={groups} />
))}
</MultiSelect>
在数据结构和组件分解方面提出了很好的观点,因此我建议将这个答案与此结合起来。我的理解是您无法修改 API 负载响应,因此您需要从 React 应用程序中执行此操作。
我们的想法是通过遍历您的 API 响应数组来创建一个新对象,并为每个项目查看它的组是否存在于您的这个新对象上。在这种情况下,新对象由 header
值标识。如果该组存在,则将数据附加到 headerData
数组,否则,根据 header
值创建一个新组,并将 headerData
设置为一个数组,并将数据作为第一项.
React.useEffect(() => {
// Desired data format (to match @j-low answer)
// [
// {
// header: 1,
// headerData: [{ id: 1, value: 1}, { id: 4, value: 4}]
// }
// ]
// `someData` is the value from API
const formattedData = someData.reduce((acc, val) => {
// see if group already exists
const group = acc.find(({ header }) => header === val.header);
if (group) {
// add to existing group
group.headerData.push(val);
} else {
// create new group
acc.push({
header: val.header,
headerData: [val]
});
}
return acc;
}, []);
// `data` stat to be mapped over in component render
setData(formattedData);
}, []);
我在上面的每个组 headerData
中包含了整个 val
对象,但如果您只想包含特定属性,则由您决定。
使用代码对齐问题的 CodeSandbox 工作示例:
我正在尝试复制像 this. 这样的下拉菜单。
这是我的代码:
const data = [
{
id: 1,
header:1,
value:1
},
{
id: 2,
header:2,
value:2
},
{
id: 3,
header:2,
value:3
},
{
id: 4,
header:1,
value:4
}
];
useEffect(() => {
setData(data)
}[])
const renderSelectGroup = wfItem => {
const items =
<MenuItem key={wfItem.value} value={wfItem.value} >
{wfItem.value}
</MenuItem>
return [<ListSubheader>{wfItem.header}</ListSubheader>, items];
// return [items]
};
return (
<Col className="col-lg-3 col-sm-3 col-xs-3 col-md-3 mr-auto mb-2">
<FormControl variant="outlined" size="small" fullWidth>
<InputLabel>Dropdown With Grouping</InputLabel>
<MuiSelect
label="Dropdown with Grouping"
value={selectValuesWM}
onChange={handleWMChange}
>
{data.map(p => renderSelectGroup(p))}
</MuiSelect>
</FormControl>
</Col>
)
所以渲染后看起来像这样:
Header 1 |
---|
Value 1 |
Header 2 |
Value 2 |
Header 2 |
Value 3 |
Header 1 |
Value 4 |
什么时候应该是这样的:
Header 1 |
---|
Value 1 |
Value 4 |
Header 2 |
Value 2 |
Value 3 |
数据没有嵌套,所以我不确定我应该在这里采取哪些步骤来获得预期的结果。非常感谢任何帮助。
最好准确获取您需要的数据,然后相应地显示 UI。收到数据后,您可以使用找到的以下函数按 header 对数据进行分组 here:
let filteredData = [];
_data.forEach((r) => {
if (!filteredData[r.header]) filteredData[r.header] = [];
filteredData[r.header].push(r);
});
const groupedData = filteredData.filter(() => true);
这将为您提供以下数据:
const groupedData = [
[{ id: 1, header: 1, value: 1}, { id: 4, header: 1, value: 4}]
[{ id: 2, header: 2, value: 2}, { id: 3, header: 2, value: 3}]
];
从概念上讲,您可以这样渲染它:
{groupedData.map((groups) => (
<>
<h2>Header {groups[0].header}</h2>
{groups.map(({ id, value }) => (
<p>
id: {id} value: {value}
</p>
))}
</>
))}
为了匹配您的方法,您的 SelectGroup
组件可以如下所示:
const SelectGroup = ({ header, groups }) => (
<>
<ListSubheader>Header: {header}</ListSubheader>
{groups.map(({ id, value }) => (
<MenuItem key={id} value={value}>
id: {id} value: {value}
</MenuItem>
))}
</>
);
// and use it as such:
<MuiSelect>
{groupedData.map((groups) => (
<SelectGroup header={groups[0].header} groups={groups} />
))}
</MultiSelect>
我们的想法是通过遍历您的 API 响应数组来创建一个新对象,并为每个项目查看它的组是否存在于您的这个新对象上。在这种情况下,新对象由 header
值标识。如果该组存在,则将数据附加到 headerData
数组,否则,根据 header
值创建一个新组,并将 headerData
设置为一个数组,并将数据作为第一项.
React.useEffect(() => {
// Desired data format (to match @j-low answer)
// [
// {
// header: 1,
// headerData: [{ id: 1, value: 1}, { id: 4, value: 4}]
// }
// ]
// `someData` is the value from API
const formattedData = someData.reduce((acc, val) => {
// see if group already exists
const group = acc.find(({ header }) => header === val.header);
if (group) {
// add to existing group
group.headerData.push(val);
} else {
// create new group
acc.push({
header: val.header,
headerData: [val]
});
}
return acc;
}, []);
// `data` stat to be mapped over in component render
setData(formattedData);
}, []);
我在上面的每个组 headerData
中包含了整个 val
对象,但如果您只想包含特定属性,则由您决定。
使用代码对齐问题的 CodeSandbox 工作示例: