渲染了比之前渲染更多的钩子。关于隐藏或显示菜单项

问题描述 投票:0回答:1

import navigationConfig from "configs/NavigationConfig";

import useAuth from "hooks/useAuth";
import useUserType from "hooks/useType";

const { SubMenu } = Menu;
const { useBreakpoint } = Grid;



const setLocale = (isLocaleOn, localeKey) =>
  isLocaleOn ? <IntlMessage id={localeKey} /> : localeKey.toString();

const setDefaultOpen = (key) => {
  let keyList = [];
  let keyString = "";
  if (key) {
    const arr = key.split("-");
    for (let index = 0; index < arr.length; index++) {
      const elm = arr[index];
      index === 0 ? (keyString = elm) : (keyString = `${keyString}-${elm}`);
      keyList.push(keyString);
    }
  }
  return keyList;
};

const useConstructor = (callBack = () => { }) => {
  const [hasBeenCalled, setHasBeenCalled] = useState(false);
  const data = callBack();
  if (hasBeenCalled) return data;
  setHasBeenCalled(true);
  return data;
};

const SideNavContent = (props) => {
  const {
    sideNavTheme,
    routeInfo,
    hideGroupTitle,
    localization,
    onMobileNavToggle,
  } = props;
  

  const data = useConstructor(() => {

    return navigationConfig;

  });

  const IsAuth = (premission) => {
    const { isAuth: isAuthenticated } = useAuth([premission]);
    return isAuthenticated;
  };

  const ExactAuth = (premission) => {
    const { exact } = useAuth([premission]);
    return exact;
  };

  const IsType = (requiredType) => {
    const { hasRequiredType } = useUserType([requiredType]);
    return hasRequiredType;
  };

  const isMobile = !utils.getBreakPoint(useBreakpoint()).includes("lg");
  const closeMobileNav = () => {
    if (isMobile) {
      onMobileNavToggle(false);
    }
  };

  return (
    <Menu
      theme={sideNavTheme === SIDE_NAV_LIGHT ? "light" : "dark"}
      mode="inline"
      style={{ height: "100%", borderRight: 0 }}
      defaultSelectedKeys={[routeInfo?.key]}
      defaultOpenKeys={setDefaultOpen(routeInfo?.key)}
      className={hideGroupTitle ? "hide-group-title" : ""}
    >
      {data.map((menu) =>
        menu.submenu.length > 0 ? (
          menu.exactAuth !== true ? (
            IsAuth(menu.premission) ? (
              (menu.userType === undefined || IsType(menu.userType)) ? (
                <Menu.ItemGroup
                  key={menu.key}
                  title={setLocale(localization, menu.title)}
                >
                  {menu.submenu.map((subMenuFirst) =>
                    subMenuFirst.submenu.length > 0 ? (
                      IsAuth(subMenuFirst.premission) ? (
                        <SubMenu
                          icon={
                            subMenuFirst.icon ? (
                              <Icon type={subMenuFirst?.icon} />
                            ) : null
                          }
                          key={subMenuFirst.key}
                          title={setLocale(localization, subMenuFirst.title)}
                        >
                          {subMenuFirst.submenu.map((subMenuSecond) =>
                            IsAuth(subMenuSecond.premission) ? (
                              <Menu.Item key={subMenuSecond.key}>
                                {subMenuSecond.icon ? (
                                  <Icon type={subMenuSecond?.icon} />
                                ) : null}
                                <span>
                                  {setLocale(localization, subMenuSecond.title)}
                                </span>
                                <Link
                                  onClick={() => closeMobileNav()}
                                  to={subMenuSecond.path}
                                />
                              </Menu.Item>
                            ) : null
                          )}
                        </SubMenu>
                      ) : null
                    ) : IsAuth(subMenuFirst.premission) ? (
                      (subMenuFirst.userType === undefined || IsType(subMenuFirst.userType)) ? (
                        <Menu.Item key={subMenuFirst.key}>
                          {subMenuFirst.icon ? (
                            <Icon type={subMenuFirst.icon} />
                          ) : null}
                          <span>{setLocale(localization, subMenuFirst.title)}</span>
                          <Link
                            onClick={() => closeMobileNav()}
                            to={subMenuFirst.path}
                          />
                        </Menu.Item>
                      ) : null
                    ) : null
                  )}
                </Menu.ItemGroup>
              ) : null
            ) : null
          ) : ExactAuth(menu.premission) ? (
            <Menu.ItemGroup
              key={menu.key}
              title={setLocale(localization, menu.title)}
            >
              {menu.submenu.map((subMenuFirst) =>
                subMenuFirst.submenu.length > 0 ? (
                  IsAuth(subMenuFirst.premission) ? (
                    <SubMenu
                      icon={
                        subMenuFirst.icon ? (
                          <Icon type={subMenuFirst?.icon} />
                        ) : null
                      }
                      key={subMenuFirst.key}
                      title={setLocale(localization, subMenuFirst.title)}
                    >
                      {subMenuFirst.submenu.map((subMenuSecond) =>
                        IsAuth(subMenuSecond.premission) ? (
                          <Menu.Item key={subMenuSecond.key}>
                            {subMenuSecond.icon ? (
                              <Icon type={subMenuSecond?.icon} />
                            ) : null}
                            <span>
                              {setLocale(localization, subMenuSecond.title)}
                            </span>
                            <Link
                              onClick={() => closeMobileNav()}
                              to={subMenuSecond.path}
                            />
                          </Menu.Item>
                        ) : null
                      )}
                    </SubMenu>
                  ) : null
                ) : IsAuth(subMenuFirst.premission) ? (
                  <Menu.Item key={subMenuFirst.key}>
                    {subMenuFirst.icon ? (
                      <Icon type={subMenuFirst.icon} />
                    ) : null}
                    <span>{setLocale(localization, subMenuFirst.title)}</span>
                    <Link
                      onClick={() => closeMobileNav()}
                      to={subMenuFirst.path}
                    />
                  </Menu.Item>
                ) : null
              )}
            </Menu.ItemGroup>
          ) : null
        ) : IsAuth(menu.premission) ? (
          <Menu.Item key={menu.key}>
            {menu.icon ? <Icon type={menu?.icon} /> : null}
            <span>{setLocale(localization, menu?.title)}</span>
            {menu.path ? (
              <Link onClick={() => closeMobileNav()} to={menu.path} />
            ) : null}
          </Menu.Item>
        ) : null
      )}
    </Menu>
  );
};


const MenuContent = (props) => {
  return props.type === NAV_TYPE_SIDE ? (
    <SideNavContent {...props} />
  ) : (
    <TopNavContent {...props} />
  );
};

const mapStateToProps = ({ theme }) => {
  const { sideNavTheme, topNavColor } = theme;
  return { sideNavTheme, topNavColor };
};

export default connect(mapStateToProps, { onMobileNavToggle })(MenuContent);

这是我的菜单内容组件。

import {
  DashboardOutlined,
  UserOutlined,
  TeamOutlined,
  VideoCameraOutlined,
  CalendarOutlined,
  SettingOutlined,
  DollarOutlined,
  FileTextOutlined,
  PlusOutlined,
  BankOutlined
} from '@ant-design/icons';
import { APP_PREFIX_PATH } from 'configs/AppConfig'
import useUserType from "hooks/useType";

const IsType = (requiredType) => {
  const { hasRequiredType } = useUserType([requiredType]);
  return hasRequiredType;
};

const NewSideBar = [
  {
    key: 'dashboard',
    path: `${APP_PREFIX_PATH}/dashboard`,
    title: 'sidenav.dashboard',
    icon: DashboardOutlined,
    breadcrumb: false,
    submenu: [
      {
        key: 'dashboard',
        path: `${APP_PREFIX_PATH}/dashboard`,
        title: 'sidenav.dashboard',
        icon: DashboardOutlined,
        premission:['ROLE_CLIENT'],
        breadcrumb: false,
        submenu: []
      },
      {
        key: 'dashboard',
        path: `${APP_PREFIX_PATH}/admin/home`,
        title: 'sidenav.dashboard',
        icon: DashboardOutlined,
        breadcrumb: false,
        premission:['ROLE_ADMIN'],
        submenu: []
      },
    ]
  },
  {
    key: 'meetings',
    path: `${APP_PREFIX_PATH}/meetings`,
    title: 'meetings',
    icon: DashboardOutlined,
    breadcrumb: false,
    premission:['ROLE_CLIENT'],
    submenu: [
      {
        key: 'meetings',
        path: `${APP_PREFIX_PATH}/meetings/all`,
        title: 'meetings',
        icon: VideoCameraOutlined,
        breadcrumb: false,
        submenu: []
      },
      {
        key: 'calendar',
        path: `${APP_PREFIX_PATH}/meetings/calendar`,
        title: 'calendar',
        icon: CalendarOutlined,
        breadcrumb: false,
        submenu: []
      }
    ]
  },
  {
    key: 'users',
    path: `${APP_PREFIX_PATH}/users`,
    title: 'Members',
    icon: TeamOutlined,
    breadcrumb: false,
    premission:['ROLE_CLIENT'],
    submenu: [
      {
        key: 'users',
        path: `${APP_PREFIX_PATH}/users/member`,
        title: 'members',
        icon: UserOutlined,
        submenu: [],
        breadcrumb: false,
      }
    ]
  },
  {
    key: 'teams',
    path: `${APP_PREFIX_PATH}/team`,
    title: 'Teams',
    icon: TeamOutlined,
    breadcrumb: false,
    premission:['ROLE_CLIENT'],
    userType:"owner",
    submenu: [
      {
        key: 'add_team',
        path: `${APP_PREFIX_PATH}/team/add`,
        title: 'Add team',
        icon: PlusOutlined,
        breadcrumb: false,
        submenu: [],
      },
      {
        key: 'team_list',
        path: `${APP_PREFIX_PATH}/team/list`,
        title: 'Team list',
        icon: TeamOutlined,
        breadcrumb: false,
        submenu: []
      },
    ]
  },
  {
    key: 'users',
    path: `${APP_PREFIX_PATH}/users`,
    title: 'Users',
    breadcrumb: false,
    premission:['ROLE_ADMIN'],
    submenu: [
      {
        key: 'add_user',
        path: `${APP_PREFIX_PATH}/admin/users/add`,
        title: 'Add user',
        icon: PlusOutlined,
        submenu: [],
        breadcrumb: false,
      },
      {
        key: 'users',
        path: `${APP_PREFIX_PATH}/admin/users/list`,
        title: 'User list',
        icon: TeamOutlined,
        submenu: [],
        breadcrumb: false,
      },
    ]
  },
  {
    key: 'organisation',
    path: `${APP_PREFIX_PATH}/organisations`,
    title: 'Users',
    breadcrumb: false,
    premission:['ROLE_ADMIN'],
    submenu: [
      {
        key: 'add_organisation',
        path: `${APP_PREFIX_PATH}/admin/organisations/add`,
        title: 'Add organisation',
        icon: PlusOutlined,
        submenu: [],
        breadcrumb: false,
      },
      {
        key: 'organisations',
        path: `${APP_PREFIX_PATH}/admin/organisations/list`,
        title: 'Organisation list',
        icon: BankOutlined,
        submenu: [],
        breadcrumb: false,
      },
    ]
  },
  {
    key: 'settings',
    path: `${APP_PREFIX_PATH}`,
    title: 'settings',
    icon: TeamOutlined,
    breadcrumb: false,
    premission:['ROLE_CLIENT'],
    submenu: [
      {
        key: 'settings',
        path: `${APP_PREFIX_PATH}/setting`,
        title: 'settings',
        icon: SettingOutlined,
        breadcrumb: false,
        submenu: []
      }
    ]
  },
  {
    key: 'pricing',
    path: `${APP_PREFIX_PATH}/plan`,
    title: 'pricing',
    icon: DashboardOutlined,
    breadcrumb: false,
    premission:['ROLE_CLIENT'],
    submenu: [
      {
        key: 'pricing',
        path: `${APP_PREFIX_PATH}/plan`,
        title: 'pricing',
        icon: DollarOutlined,
        breadcrumb: false,
        submenu: []
      },
      
    ]
  },
  {
    key: 'plan',
    path: `${APP_PREFIX_PATH}/admin/plan`,
    title: 'Plan',
    icon: DollarOutlined,
    breadcrumb: false,
    premission:['ROLE_ADMIN'],
    submenu: [
      {
        key: 'add-plan',
        path: `${APP_PREFIX_PATH}/admin/plan/add`,
        title: 'Add plan',
        icon: PlusOutlined ,
        breadcrumb: false,
        submenu: []
      },
      {
        key: 'plans',
        path: `${APP_PREFIX_PATH}/admin/plan/list`,
        title: 'Plan list',
        icon: FileTextOutlined,
        breadcrumb: false,
        submenu: []
      },
    ]
  },
  {
    key: 'feature',
    path: `${APP_PREFIX_PATH}/admin/feature`,
    title: 'feature',
    icon: DollarOutlined,
    breadcrumb: false,
    premission:['ROLE_ADMIN'],
    submenu: [
      {
        key: 'add-feature',
        path: `${APP_PREFIX_PATH}/admin/feature/add`,
        title: 'Add feature',
        icon: PlusOutlined ,
        breadcrumb: false,
        submenu: []
      },
      {
        key: 'features',
        path: `${APP_PREFIX_PATH}/admin/feature/list`,
        title: 'Feature list',
        icon: FileTextOutlined,
        breadcrumb: false,
        submenu: []
      },
    ]
  },
]

const navigationConfig = [
  ...NewSideBar
]



export default navigationConfig;

这是 NavigationConfig 组件。

import React, { useEffect, useState } from "react";
import { Menu, Dropdown, Button, Avatar, Modal, Form, Input, message, Upload, Tooltip } from "antd";
import { useHistory } from "react-router-dom/cjs/react-router-dom.min";
import { connect, useDispatch } from "react-redux";
import { ChangeOrganisation } from "redux/actions";
import { PlusOutlined, RollbackOutlined, BankOutlined } from '@ant-design/icons';
import OrganisationService from "services/OrganisationService";
import { env } from "configs/EnvironmentConfig";

export const NavOrganisation = (props) => {
    const { ChangeOrganisation, name, icon, id, username } = props;

    const [ownedOrganisation, setOwnedOrganisation] = useState([]);
    const [joinedOrganisation, setJoinedOrganisation] = useState([]);
    const history = useHistory();
    const dispatch = useDispatch();
    const [showJoinModel, setShowJoinModel] = useState(false)


    useEffect(() => {
        OrganisationService.GetOwnedOrganisation().then((response) => {
            setOwnedOrganisation(response.data);
        });
        OrganisationService.GetJoinedOrganisations().then((response) => {
            setJoinedOrganisation(response.data);
        });
    }, [id]);

    const userTypeInOrganisation = (organisation) => {
        return organisation.members.find((member) => member.username === username).type;
    }

    const JoinModel = props => {
        const { visible, onCancel } = props
        const [form] = Form.useForm()
        const [join, setJoin] = useState(true)

        const handleSendRequest = () => {
            const { code } = form.getFieldsValue(['code']);
            OrganisationService.SendJoinRequest(code).then((resp) => {
                message.success(resp.data.message);
                onCancel();
                form.resetFields();
            });
        }

        const handleCreateOrganisation = () => {
            const { name, phone, adresse, icon } = form.getFieldsValue(['name', 'phone', 'adresse', 'icon']);
            const formData = new FormData();
            formData.append('name', name);
            formData.append('phone', phone);
            formData.append('adresse', adresse);
            if (icon && icon.fileList.length > 0) {
                formData.append('icon', icon.fileList[0].originFileObj);
            }
            OrganisationService.CreateOrganisation(formData).then((resp) => {
                message.success('Organisation created successfully');
                onCancel();
                form.resetFields();
                dispatch(ChangeOrganisation({
                    id: resp.data.id,
                    name: resp.data.name,
                    isOwner: false,
                    userType: "owner",
                    "code": resp.data.code,
                    "icon": resp.data.icon
                }));
                history.push("/app/home");
            });
        }

        return (
            <Modal
                visible={visible}
                width={650}
                closable={false}
                title={join ? 'Join Organisation' : 'Create Organisation'}
                footer={[
                    <Button key="back" className="btn btn-light" onClick={onCancel}>
                        Cancel
                    </Button>,
                    <Button type={'primary'} onClick={() => {
                        form.validateFields(['code'])
                            .then((values) => {
                                if (Object.values(values).every(value => value)) {
                                    handleSendRequest();
                                    setJoin(true);
                                } else {
                                    setJoin(true);
                                }
                            })
                            .catch(() => {
                                message.error('Please put the code');
                                setJoin(true);
                            });
                    }} >
                        Join
                    </Button>,
                    <Button
                        type={'primary'}
                        ghost
                        onClick={() => {
                            form.validateFields(['name', 'phone', 'adresse'])
                                .then((values) => {
                                    if (Object.values(values).every(value => value)) {
                                        handleCreateOrganisation();
                                        setJoin(false);
                                    } else {
                                        setJoin(false);
                                    }
                                })
                                .catch(() => {
                                    message.error('Please fill all the fields');
                                    setJoin(false);
                                });
                        }}
                    >
                        Create
                    </Button>
                ]}
                centered
            >
                <Form form={form} labelCol={{ span: 8 }}>
                    {join ?
                        <Form.Item name={'code'} label={'Organisation Code'} rules={[
                            {
                                required: true,
                                message: 'Please input the organisation code'
                            }
                        ]}>
                            <Input />
                        </Form.Item>
                        :
                        <>
                            <Form.Item name={'name'} label={'Organisation Name'} rules={[
                                {
                                    required: true,
                                    message: 'Please input the organisation name'
                                }
                            ]}>
                                <Input />
                            </Form.Item>
                            <Form.Item name={'phone'} label={'Organisation phone'} rules={[
                                {
                                    required: true,
                                    message: 'Please input the organisation phone'
                                }
                            ]}>
                                <Input />
                            </Form.Item>
                            <Form.Item name={'adresse'} label={'Organisation addresse'} rules={[
                                {
                                    required: true,
                                    message: 'Please input the organisation address'
                                }
                            ]}>
                                <Input />
                            </Form.Item>
                            <Form.Item name={'icon'} label={'Organisation icon'}>
                                <Upload action="/upload.do" listType="picture-card">
                                    <button
                                        style={{
                                            border: 0,
                                            background: 'none',
                                        }}
                                        type="button"
                                    >
                                        <PlusOutlined />
                                        <div
                                            style={{
                                                marginTop: 8,
                                            }}
                                        >
                                            Upload
                                        </div>
                                    </button>
                                </Upload>
                            </Form.Item>
                        </>
                    }
                </Form>
            </Modal>
        )
    }

    const organisationMenu = (
        <div className="nav-profile nav-dropdown">
            <div className="nav-profile-header d-flex justify-content-between">
                <div className="ml-2" onClick={() => setShowJoinModel(true)} style={{ cursor: 'pointer' }}>
                    <Avatar size={30}><PlusOutlined /></Avatar>
                    <span className="ml-2">Join Organisation</span>
                </div>
                <div className="ml-2" onClick={(e) => {
                    dispatch(ChangeOrganisation({
                        id: "",
                        name: "",
                        isOwner: false,
                        userType: "",
                        "code": "",
                    }))
                }}
                    style={{ cursor: 'pointer' }}
                >
                    <Tooltip title="Back to Profile">
                        <Avatar size={30} style={{ backgroundColor: 'white' }}>
                            <RollbackOutlined style={{ color: 'black' }} />
                        </Avatar>

                    </Tooltip>
                </div>
            </div>
            <div className="nav-profile-body">
                <Menu>
                    {ownedOrganisation.map((organisation) => (
                        <Menu.Item
                            key={organisation.id}
                            onClick={(e) => {
                                dispatch(ChangeOrganisation({
                                    id: organisation.id,
                                    name: organisation.name,
                                    isOwner: true,
                                    userType: "owner",
                                    "code": organisation.code,
                                    "icon": organisation.icon,
                                    "phone": organisation.phone,
                                }));
                                history.push("/app/home");
                            }}
                            className="d-flex justify-content-between"
                        >
                            <div>
                                {organisation.icon ?
                                    <Avatar src={env.IMG_SRC + organisation.icon}></Avatar>
                                    :
                                    <Avatar>{organisation.name}</Avatar>
                                }
                                <span className="ml-2">{organisation.name}</span>
                            </div>
                            {/* <span>{organisation.code}</span> */}

                        </Menu.Item>
                    ))}
                    {joinedOrganisation.map((organisation) => (
                        <Menu.Item
                            key={organisation.id}
                            onClick={(e) => {
                                dispatch(ChangeOrganisation({
                                    id: organisation.id,
                                    name: organisation.name,
                                    isOwner: false,
                                    userType: userTypeInOrganisation(organisation),
                                    "code": organisation.code,
                                    "icon": organisation.icon,
                                    "phone": organisation.phone,
                                }));
                                history.push("/app/home");
                            }}
                            className="d-flex justify-content-between"
                        >
                            <div>
                                {organisation.icon ?
                                    <Avatar src={env.IMG_SRC + organisation.icon}></Avatar>
                                    :
                                    <Avatar>{organisation.name}</Avatar>
                                }
                                <span className="ml-2">{organisation.name}</span>
                            </div>
                            {/* <span>{organisation.code}</span> */}
                        </Menu.Item>
                    ))}
                </Menu>
            </div>

        </div>
    );

    return (
        <>
            {ownedOrganisation.length === 0 && joinedOrganisation.length === 0 ?
                <Button className="mt-3" onClick={() => setShowJoinModel(true)}>
                    <PlusOutlined />
                    <span>New</span>
                </Button>
                :
                <Dropdown
                    placement="bottomRight"
                    overlay={organisationMenu}
                    trigger={["click"]}
                >
                    <Menu className="d-flex align-item-center" mode="horizontal">
                        <Menu.Item>
                            <Tooltip title="Organisations">
                                {id ?
                                    icon ?
                                        <Avatar src={env.IMG_SRC + icon}></Avatar>
                                        :
                                        <Avatar>{name}</Avatar>
                                    :
                                    <Avatar size={50} className="mb-1 bg-white" icon={<BankOutlined style={{ color: 'black' }} />} />
                                }
                            </Tooltip>
                        </Menu.Item>
                    </Menu>
                </Dropdown>
            }
            <JoinModel
                visible={showJoinModel}
                onCancel={() => setShowJoinModel(false)}
            />
        </>
    );
};

const mapStateToProps = ({ org, auth }) => {
    const { id, name, type, isOwner, icon } = org;
    const { username } = auth;
    return { id, name, type, isOwner, icon, username };
};

export default connect(mapStateToProps, { ChangeOrganisation })(NavOrganisation);

这是 NavOrganization 组件,我可以在其中更改组织状态

问题是当我想改变 redux 存储中的组织状态时,我希望菜单内容发生变化,我的意思是隐藏或显示项目,但我收到此错误:错误:比之前的渲染期间渲染了更多的钩子。

javascript reactjs react-hooks react-redux hook
1个回答
0
投票

React hooks 应该在组件级别调用,而不是在 React 组件内部定义的函数内调用。

例如:

export const SideNavContent = (props) => {
  const IsAuth = (premission) => {
    const { isAuth: isAuthenticated } = useAuth([premission]);
    return isAuthenticated;
  };


  return <div>...</div>;
};

这里的 useAuth 是一个在函数内部使用的钩子,它本身不是一个组件。正确的方法是将 useAuth 移至 SideNavContent 组件。

如果这不能解决您的问题,请与我分享完整的堆栈跟踪。

© www.soinside.com 2019 - 2024. All rights reserved.