javascript 将状态作为属性添加到对象数组的每一项上

wrrgggsh  于 2023-03-21  发布在  Java
关注(0)|答案(1)|浏览(126)

我正在开发一个React应用,它有不同的用户类型。每个用户的菜单上都有特定的项。项可以是链接或子菜单。我想为链接项呈现按钮,为子菜单呈现折叠(react-bootstrap的)。但是,为了让Collapse正常工作,我需要在每个子菜单项上添加一个布尔状态。
所有菜单都在一个文件中分开,如下所示:

// /utils/Menus.js

export const AdminMenu = [
    { label: "Dashboard", href: "/dashboard" },
    { label: "Users", href: "#", subitems: [
        { label: "All Users", href: "/users" },
        { label: "New User", href: "/users/new" }
    ] },
    { label: "Products", href: "#", subitems: [
        { label: "All Products", href: "/products" },
        { label: "New Product", href: "/products/new" }
    ] },
    { label: "Clients", href: "#", subitems: [
        { label: "All Clients", href: "/clients" },
        { label: "New Client", href: "/clients/new" }
    ] },
    { label: "Orders", href: "/orders", subitems: [
        { label: "All Orders", href: "/orders" },
        { label: "Cancelled", href: "/orders/cancelled" },
        { label: "Latest", href: "/orders/latest" }
    ] },
    { label: "My Profile", href: "/my-profile" },
    { label: "Logout", href: "/logout" }
];

export const ClientMenu = [
    { label: "Dashboard", href: "/dashboard" },
    { label: "My Orders", href: "/orders" },
    { label: "My Profile", href: "/my-profile" },
    { label: "Logout", href: "/logout" }
];

export const ManagerMenu = [
    { label: "Dashboard", href: "/dashboard" },
    { label: "Products", href: "#", subitems: [
        { label: "All Products", href: "/products" },
        { label: "New Product", href: "/products/new" }
    ] },
    { label: "Orders", href: "/orders", subitems: [
        { label: "All Orders", href: "/orders" },
        { label: "Cancelled", href: "/orders/cancelled" },
        { label: "Latest", href: "/orders/latest" }
    ] },
    { label: "My Profile", href: "/my-profile" },
    { label: "Logout", href: "/logout" }
];

我有一个侧边栏组件,它根据登录的用户配置文件呈现菜单。在这个组件上,我想在每个具有subitems属性的菜单项上添加一个open状态属性。这样:

// /components/Sidenav.js

[...]
const [userMenu, setUserMenu] = useState([]);

useEffect(() => {
    let menu = [];

    switch(user.role){
        case "admin":
            menu = [...AdminMenu];
            break;
        case "client":
            menu = [...ClientMenu];
            break;
        case "manager":
            menu = [...ManagerMenu];
            break;
    }

    menu = menu.map((item) => {
        if(item.subitems){
            item = {...item, open: useState(false)}
        }
        return item;
    });

    setUserMenu(menu);
}, []);

return (
    <ul>
        {userMenu.map((item, i) => (
            <li key={i}>
                { item.subitems ? (
                <>
                    <button onClick={() => { item.open[1](!item.open[0]) }}>{item.label}</button>
                    <Collapse in={item.open[0]}>\
                        <ul>
                            { item.subitems.map((subitem, j) => (
                                <li key={j}>
                                    <Link href={subitem.href}>{subitem.label}</Link>
                                </li>
                            )) }
                        </ul>
                    </Collapse>
                </>
                ) : (
                    <Link href={item.href}>{item.label}</Link>
                ) }
            </li>
        ))}
    </ul>
);

因此,方法是尝试在每个有子项的项上动态添加open状态。并且,在渲染时,使用item.open[0](状态)和item.open[1](设置状态函数)来切换Collapse的状态。
问题是:React抛出了一个错误,说我不能在函数组件之外使用useState,但是当我在项目上添加open属性时,我在函数组件上的useEffect中,那么问题在哪里呢?
我已经测试了将组件上的菜单声明为常量,并在必要时直接属性化open状态,它按预期工作。

// /components/Sidenav.js

[...]
const [userMenu, setUserMenu] = useState([]);

const AdminMenu = [
    { label: "Dashboard", href: "/dashboard" },
    { label: "Users", href: "#", open: useState(false), subitems: [
        { label: "All Users", href: "/users" },
        { label: "New User", href: "/users/new" }
    ] },
    { label: "Products", href: "#", open: useState(false), subitems: [
        { label: "All Products", href: "/products" },
        { label: "New Product", href: "/products/new" }
    ] },
    { label: "Clients", href: "#", open: useState(false), subitems: [
        { label: "All Clients", href: "/clients" },
        { label: "New Client", href: "/clients/new" }
    ] },
    { label: "Orders", href: "/orders", open: useState(false), subitems: [
        { label: "All Orders", href: "/orders" },
        { label: "Cancelled", href: "/orders/cancelled" },
        { label: "Latest", href: "/orders/latest" }
    ] },
    { label: "My Profile", href: "/my-profile" },
    { label: "Logout", href: "/logout" }
];

const ClientMenu = [
    { label: "Dashboard", href: "/dashboard" },
    { label: "My Orders", href: "/orders" },
    { label: "My Profile", href: "/my-profile" },
    { label: "Logout", href: "/logout" }
];

const ManagerMenu = [
    { label: "Dashboard", href: "/dashboard" },
    { label: "Products", href: "#", open: useState(false), subitems: [
        { label: "All Products", href: "/products" },
        { label: "New Product", href: "/products/new" }
    ] },
    { label: "Orders", href: "/orders", open: useState(false), subitems: [
        { label: "All Orders", href: "/orders" },
        { label: "Cancelled", href: "/orders/cancelled" },
        { label: "Latest", href: "/orders/latest" }
    ] },
    { label: "My Profile", href: "/my-profile" },
    { label: "Logout", href: "/logout" }
];

useEffect(() => {
    switch(user.role){
        case "admin":
            setUserMenu(AdminMenu);
            break;
        case "client":
            setUserMenu(ClientMenu);
            break;
        case "manager":
            setUserMenu(ManagerMenu);
            break;
    }
}, []);

return (
    [...]
);

但是这使得我在显示用户菜单时需要重复菜单常量,这不是最好的方法。
那么,如何在菜单项上添加open状态,以动态呈现链接或折叠?

g52tjvyc

g52tjvyc1#

我认为将其作为一个组件会更简单一些,其状态如下(我在下面自由排版,所以代码可能无法正常工作)

const CollapseMenu = ({
  item,
}) => {
  const [isOpen, setIsOpen] = useState(false)

  return <>
          <button onClick={() => { setIsOpen(!isOpen) }}>{item.label}</button>
          <Collapse in={isOpen}>\
              <ul>
                  { item.subitems.map((subitem, j) => (
                      <li key={j}>
                          <Link href={subitem.href}>{subitem.label}</Link>
                      </li>
                  )) }
              </ul>
          </Collapse>
  </>
}

相关问题