在React中创建一个任意层数的递归列表菜单

376 阅读3分钟

递归在编程中可能是一个棘手的概念。在React这样的视图库中,这个挑战似乎更大。今天,我们将使用递归来创建一个任意层数的菜单。此外,我们将使它能够在任何层次上切换子菜单的显示。

入门

为了开始工作,我们可以首先定义我们的菜单结构。重要的是,递归只有在我们对菜单中的每一层都一视同仁的情况下才会起作用,这意味着它应该有相同的结构,一直向下。为了达到这个目的,我们可以决定每个菜单项将有一个title 和一个children 的数组。每个子项将遵循相同的格式,一直向下。

对于这篇文章,我们将使用以下的菜单结构。

- Item 1
  - Item 1.1
    - Item 1.1.1
  - Item 1.2
- Item 2
  - Item 2.1

而我们可以将其表示为一个JavaScript对象,一路向下的界面都是一致的。

const menu = [
  {
    title: 'Item 1',
    children: [
      {
        title: 'Item 1.1',
        children: [
          {
            title: 'Item 1.1.1',
          },
        ],
      },
      {
        title: 'Item 1.2',
      },
    ],
  },
  {
    title: 'Item 2',
    children: [
      {
        title: 'Item 2.1',
      },
    ],
  },
];

显示顶层

让我们来显示我们菜单的顶层。我们将创建一个Menu 组件。这个组件将接受我们的menu 数组作为一个参数。因此,无论我们想在哪里渲染菜单,它都会看起来像这样。

<Menu items={menu} />

Menu 组件中,我们将mapmenu 数组中的每个项目上,并在一个列表项中显示每个项目title 。到目前为止都是相当初级的React!

function Menu({ items }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.title}>{item.title}</li>
      ))}
    </ul>
  );
}

现在我们有了一个双项数组。我们的下一个挑战是渲染下一级的孩子。

显示下一级(以及下一级和下一级)。

事实证明,以递归方式显示下面的层级并不像我们担心的那样是个挑战!因为我们的数据结构是按照我的想法设计的。由于我们将数据结构设计成一路一致,我们可以简单地将一个项目的children 数组传递给另一个Menu 调用,如果children 存在。这就是我的意思!

function Menu({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.title}>{item.title}
        {item.children && <Menu items={item.children}>}
        </li>
      ))}
    </ul>
  );
}

切换菜单项

我们的列表可能会变得笨重,所以我们要在顶层开始折叠,并让用户能够使用+- 按钮来切换子项的显示。要做到这一点,我们可以简单地让我们菜单的每一级记住任何一组子菜单的显示状态。

例如,顶层菜单将有一些状态,知道是否显示Item 1 的子女,以及是否显示Item 2 的子女。

让我们来实现这个逻辑并讨论一下。

import React, { useState } from 'react';

function Menu({ items }) {
  const [displayChildren, setDisplayChildren] = useState({});

  return (
    <ul>
      {items.map((item) => {
        return (
          <li key={item.title}>
            {item.title}{' '}
            {item.children && (
              <button
                onClick={() => {
                  setDisplayChildren({
                    ...displayChildren,
                    [item.title]: !displayChildren[item.title],
                  });
                }}
              >
                {displayChildren[item.title] ? '-' : '+'}
              </button>
            )}
            {displayChildren[item.title] && item.children && <Menu items={item.children} />}
          </li>
        );
      })}
    </ul>
  );
}

当我们开始的时候,我们的每个Menu 组件都会有一块叫做displayChildren 的状态被设置为{} 。如果你在顶层点击Item 1 旁边的+ 按钮,displayChildren 的状态现在将等于{ "Item 1": true } 。这将是有状态的displayChildren 对象在我们的菜单的每一层的工作方式!

总结

希望这能让你对React中的递归工作有一些了解。只要仔细计划一下,我们就可以在React中相对轻松地使用递归数据结构了!