使 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 工作示例: