【问题标题】:How to conditionally group HTML tags in React如何在 React 中对 HTML 标签进行有条件的分组
【发布时间】:2022-01-21 00:20:30
【问题描述】:

我在这样的循环中进行模板渲染

{
    category.menuIds.map((menuId, idx) => (
        <TemplateMenu
            index={idx}
            key={menuId}
            menu={this.props.menus[menuId]}
            menuLayout={category.menu_layout}
        />
    ));
}

它会呈现&lt;div&gt; 的列表,如下所示:

<div id="m0" class="Menu">...</div>
<div id="m1" class="Menu">...</div>
<div id="m2" class="Menu">...</div>
<div id="m3" class="Menu">...</div>
<div id="m4" class="Menu">...</div>
<div id="m5" class="Menu">...</div>
<div id="m6" class="Menu">...</div>
<div id="m7" class="Menu">...</div>
<div id="m8" class="Menu">...</div>
<div id="m9" class="Menu">...</div>

我想在this.props.index % 5 !== 0 的条件下将每 4 个&lt;div&gt; 分组以获取下面的 HTML:

<div id="m0" class="Menu">...</div>
<div class="MenuRight">
    <div id="m1" class="Menu">...</div>
    <div id="m2" class="Menu">...</div>
    <div id="m3" class="Menu">...</div>
    <div id="m4" class="Menu">...</div>
</div>
<div id="m5" class="Menu">...</div>
<div class="MenuRight">
    <div id="m6" class="Menu">...</div>
    <div id="m7" class="Menu">...</div>
    <div id="m8" class="Menu">...</div>
    <div id="m9" class="Menu">...</div>
</div>

任何帮助将不胜感激。

[编辑]

index 没关系。它可以与现在相同或相对于该组。为了更清楚,我只是在示例代码中描述了它。

this.props.index % 5 === 0 条件下,元素&lt;div class="Menu"&gt; 将是独立的,否则其他元素将与&lt;div class="MenuRight"&gt; 分组。

【问题讨论】:

  • index 属性应该在TemplateEntry 组件上是什么?和现在一样,还是相对于它所在的组?
  • 您所展示的不仅仅是将它们分成四人一组,它比这更复杂(先做一个,然后是一组四人,然后是另一个,然后是另一组四人), ...这是故意的吗?
  • @T.J.Crowder index 被上一层传递——循环的索引category.menuIds.map
  • this.props.index % 5 === 0 条件下,元素&lt;div class="Menu"&gt; 将是独立的,否则其他元素将与&lt;div class="MenuRight"&gt; 组合在一起。有意义吗?
  • 是的,谢谢——请使用“编辑”链接将其放入问题中(以及index 无关紧要的信息)。

标签: javascript arrays reactjs


【解决方案1】:

您可以将categories.menuIds 数组缩减为类似于上述结构的二维数组。例如。 [[0], [1, 2, 3, 4], [5], [6, 7, 8, 9]...]。现在偶数索引包含 MenuItem [0],奇数索引包含 MenuRight [1, 2, 3, 4]。现在我们可以使用Array.map() 有条件地渲染它。

工作示例: Demo Link

首先减少categories.menuIds数组

const menuIds = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];

const transformed = menuIds.reduce((acc, curr, idx) => {
  if (idx % 5 === 0) {
    const i = Math.floor(idx / 5) * 2;
    acc[i] = [curr];
  } else {
    const parentIdx = Math.floor(idx / 5) * 2 + 1;
    acc[parentIdx] = acc[parentIdx] || [];
    acc[parentIdx] = [...acc[parentIdx], curr];
  }

  return acc;
}, []);

console.log(transformed);

使用Array.map()React 中渲染它

<div className="App">
  {transformed.map((item, idx) => {
    return (
      <>
        {idx % 2 === 0 && (
          <div key={idx} className="Menu">
            {item}
          </div>
        )}
        {idx % 2 === 1 && (
          <div key={idx} className="MenuRight">
            {item.map((menuRight, i) => (
              <div key={i} className="Menu">
                {menuRight}
              </div>
            ))}
          </div>
        )}
      </>
    );
  })}
</div>

【讨论】:

    【解决方案2】:

    在标签已经映射到TemplateMenu 的平面列表之后重新组合标签会很复杂,因为每个元素都不知道根据其父级逻辑的位置。

    为了解决您的问题,我会在将元素映射到最终元素之前对它们进行分组,然后将它们呈现为单个 TemplateMenu 或分组 MenuRight

    例如,这是我认为对它们进行分组的最快方法,但当然还有许多其他算法您可能更喜欢:

    const groupedIds = menuIds.reduce((groups: any[], id: any, index: number) => {
        if (index % 5 === 0) {
          groups[Math.floor(index / 5) * 2] = [id];
        } else {
          const groupIndex = Math.floor(index / 5) * 2 + 1;
          if (groups[groupIndex]) {
            groups[groupIndex].push(id);
          } else {
            groups[groupIndex] = [id];
          }
        }
    
        return groups;
      }, []);
    

    这将产生一个像[[0],[1,2,3,4],[5],[6,7...]...] 这样的ID 数组。然后您可以使用这些组以不同的方式呈现它们。

    这是一个工作示例,其中的问题源自您 (also on CodeSandbox):

    function TemplateMenu({ index, id }: { index: number; id: any }) {
      return (
        <div id={`m${index}`} className="Menu">
          {id}
        </div>
      );
    }
    
    /*export default*/ function App() {
      const menuIds = ["single 1", 2, 3, 4, 5, "single 2", "a", "b"];
    
      const groupedIds = menuIds.reduce((groups: any[], id: any, index: number) => {
        if (index % 5 === 0) {
          groups[Math.floor(index / 5) * 2] = [id];
        } else {
          const groupIndex = Math.floor(index / 5) * 2 + 1;
          if (groups[groupIndex]) {
            groups[groupIndex].push(id);
          } else {
            groups[groupIndex] = [id];
          }
        }
    
        return groups;
      }, []);
    
      return (
        <div className="App">
          {/*{menuIds.map((id, idx) => {
            return <TemplateMenu key={id} index={idx} id={id} />;
          })}*/}
          {groupedIds.map((group, idx) => {
            if (group.length === 1) {
              return <TemplateMenu key={group[0]} index={idx} id={group[0]} />;
            } else {
              return (
                <div className="MenuRight">
                  {group.map((id, idx) => {
                    return <TemplateMenu key={id} index={idx} id={id} />;
                  })}
                </div>
              );
            }
          })}
        </div>
      );
    }
    
    ReactDOM.render(<App />, document.getElementById("root"));
    .Menu {
        outline: 1px solid red;
    }
    
    .MenuRight {
        outline: 1px solid blue;
        padding: 4px;
    }
    <div id="root"></div>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

    【讨论】:

    • 请将可运行的示例放在这里,现场,而不是偏移。堆栈片段([&lt;&gt;] 工具栏按钮)。 Stack Snippets 支持 React,包括 JSX; here's how to do one。将其全部保留在现场的四个原因: 1. 它确保您不会意外遗漏重要代码。 2.人们不应该去场外得到完整的答案。 3. 某些网站被某些用户屏蔽。 4. 链接失效,使问题及其答案对未来的人们毫无用处。
    • 第五个原因:它让人们非常容易看到您所做的工作。 :-D 如果您不介意我将相关的额外位从 CodeSandbox 复制到 SO(许可证的东西,需要您的许可),我很乐意为您做这件事(例如)?只需@ping 我。
    • 感谢您的提示!我仍然可以回答 SO ^^' 我不介意你是否为我解决这个答案。非常感谢。
    • 完成!下次你可以follow those instructions 和这个例子,这样你就可以自己把它放在答案中。编码愉快!
    【解决方案3】:

    你可以用一个简单的循环来做到这一点,这对我来说是迄今为止最清晰的方法;见 cmets:

    const {menuIds} = category;
    const elements = [];
    // Include every 5th element directly, including the 0th one;
    // group the (up to) four following it
    let index = 0;
    while (index < menuIds.length) {
        // Use this entry directly, note we increment index after using it
        // in the `TemplateMenu`
        const {menuId} = menuIds[index];
        elements.push(
            <TemplateMenu
                index={index++}
                key={menuId}
                menu={this.props.menus[menuId]}
                menuLayout={category.menu_layout}
            />
        );
    
        // Follow it with up to four in a `<div class="MenuRight">`
        const following = menuIds.slice(index, index + 4);
        if (following.length) {
            // Note we increment `index` as we go
            elements.push(
                <div className="MenuRight">
                    {following.map(({menuId}) => (
                        <TemplateMenu
                            index={index++}
                            key={menuId}
                            menu={this.props.menus[menuId]}
                            menuLayout={category.menu_layout}
                        />
                    ))}
                </div>
            );
        }
    }
    
    // ...use {elements}...
    

    现场示例:

    const {useState} = React;
    
    const TemplateMenu = ({index, menu, menuLayout}) => <div className="TemplateMenu">{menu}</div>;
    
    class Example extends React.Component {
        render() {
            const category = {
                menuIds: [
                    {menuId: 0},
                    {menuId: 1},
                    {menuId: 2},
                    {menuId: 3},
                    {menuId: 4},
                    {menuId: 5},
                    {menuId: 6},
                    {menuId: 7},
                    {menuId: 8},
                    {menuId: 9},
                ],
                menu_layout: "example",
            };
            const {menuIds} = category;
            const elements = [];
            // Include every 5th element directly, including the 0th one;
            // group the (up to) four following it
            let index = 0;
            while (index < menuIds.length) {
                // Use this entry directly, note we increment index after using it
                // in the `TemplateMenu`
                const {menuId} = menuIds[index];
                elements.push(
                    <TemplateMenu
                        index={index++}
                        key={menuId}
                        menu={this.props.menus[menuId]}
                        menuLayout={category.menu_layout}
                    />
                );
    
                // Follow it with up to four in a `<div class="MenuRight">`
                const following = menuIds.slice(index, index + 4);
                if (following.length) {
                    // Note we increment `index` as we go
                    elements.push(
                        <div className="MenuRight">
                            {following.map(({menuId}) => (
                                <TemplateMenu
                                    index={index++}
                                    key={menuId}
                                    menu={this.props.menus[menuId]}
                                    menuLayout={category.menu_layout}
                                />
                            ))}
                        </div>
                    );
                }
            }
    
            // ...use {elements}...
            return <div>{elements}</div>;
        }
    };
    
    const menus = [
        "Menu 0",
        "Menu 1",
        "Menu 2",
        "Menu 3",
        "Menu 4",
        "Menu 5",
        "Menu 6",
        "Menu 7",
        "Menu 8",
        "Menu 9",
    ];
    ReactDOM.render(
        <Example menus={menus} />,
        document.getElementById("root")
    );
    .TemplateMenu {
        border: 1px solid green;
        margin: 4px;
    }
    .MenuRight {
        margin: 4px;
        padding: 4px;
        border: 1px solid red;
    }
    <div id="root"></div>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

    【讨论】:

    • 所有答案都很棒,但这个解决方案对我来说似乎更清楚。它与我当前的代码对齐。所以,我接受了这个答案。谢谢大家的帮助。
    猜你喜欢
    • 1970-01-01
    • 2017-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 2020-09-08
    • 2014-05-11
    • 2013-04-12
    相关资源
    最近更新 更多