React.js:如何在呈现页面之前先运行useEffect?

时间:2020-06-25 12:18:15

标签: javascript reactjs ecmascript-6

我正在尝试实现一个动态菜单,该菜单会根据用户角色来更改其结构

查看下面的完整代码:

import React, {
  useState,
  useLayoutEffect,
  useEffect,
  useCallback,
} from "react";
import { Drawer, IconButton, List, Avatar } from "@material-ui/core";
import {
  Inbox as InboxIcon,
  PresentToAll as PresentToAllIcon,
  ListAlt as ListAltIcon,
  Language as LanguageIcon,
  Description as DescriptionIcon,
  List as ListIcon,
  Money as MoneyIcon,
  Face as FaceIcon,
  TransferWithinAStation as TransferWithinAStationIcon,
  AttachMoney as AttachMoneyIcon,
  PersonPinCircle as PersonPinCircleIcon,
  Home as HomeIcon,
  ArrowBack as ArrowBackIcon,
  Edit as EditIcon,
  AccountBalanceWallet,
  PeopleAlt,
} from "@material-ui/icons";
import { useTheme } from "@material-ui/styles";
import { withRouter } from "react-router-dom";
import classNames from "classnames";
import useStyles from "./styles";
import SidebarLink from "./components/SidebarLink/SidebarLink";
import {
  useLayoutState,
  useLayoutDispatch,
  toggleSidebar,
} from "../../context/LayoutContext";
import Dot from "./components/Dot";
import pesonetlogo from "../../images/test-logo.png";
import { USER_PROFILE_ID_SESSION_ATTRIBUTE } from "../../services/AuthenticationService";
import ProfileMaintenanceService from "../../services/ProfileMaintenanceService";

function Sidebar({ location }) {
  var classes = useStyles();
  var theme = useTheme();

  var { isSidebarOpened, modules } = useLayoutState();
  var layoutDispatch = useLayoutDispatch();

  var [isPermanent, setPermanent] = useState(true);

  // should be in UserContext for global state
  const [profileDetails, setProfileDetails] = useState([]);
  const [profileModules, setProfileModules] = useState([]);
  // const [profileActions, setProfileActions] = useState([]);

  var structureNew = [];

  const profileList = sessionStorage.getItem(USER_PROFILE_ID_SESSION_ATTRIBUTE);

  useEffect(() => {
    modules.forEach((modulesMap, i) => {
      structureNew[i] = structure.filter(
        (structureFiltered) => structureFiltered.label == modulesMap
      );
    });
    console.log("06262020 useEffect structureNew ", structureNew)
  }, []);

  // useLayoutEffect(() => {
  //   console.log("06252020 useEffect profileDetails ", profileDetails);
  //   const modules = profileDetails.map((module) => module.module);
  //   // const actions = profileDetails
  //   //   .map((item) => item.actions.map((action) => action.action))
  //   //   .flat();
  //   setProfileModules(profileModules.concat(modules));
  //   // setProfileActions(profileActions.concat(actions));
  // }, [profileDetails]);

  // useLayoutEffect(() => {
  //   console.log("06252020 useEffect profileModules ", profileModules);
  //   structureNew = structure.filter(
  //     (structureFiltered) => structureFiltered.label == "Inward"
  //   );
  //   console.log("06252020 structureNew ", structureNew);
  // }, [profileModules]);

  // // // should be in UserContext for global use
  // const retrieveProfileDetails = useCallback(() => {
  //   const profileListArr = profileList.split(",");
  //   profileListArr.forEach((profileListArrMap) => {
  //     ProfileMaintenanceService.retrieveProfileDetails(profileListArrMap).then(
  //       // dapat makuha din menu?
  //       (response) => {
  //         console.log(
  //           "06252020 retrieveProfileDetails response.data ",
  //           response.data
  //         );
  //         setProfileDetails(response.data);
  //       }
  //     );
  //   });
  // });

  var structure = [
    { id: 0, label: "Dashboard", link: "/test/dashboard", icon: <HomeIcon /> },
    {
      id: 1,
      label: "Test1",
      link: "/test1",
      icon: <InboxIcon />,
    },
    {
      id: 2,
      label: "Test2",
      link: "/test2",
      icon: <PresentToAllIcon />,
    },
    { id: 3, type: "divider" },
    {
      id: 4,
      label: "Test3",
      link: "/test3",
      icon: <ListAltIcon />,
      children: [
        {
          label: "Test4",
          link: "/test4",
          icon: <LanguageIcon />,
        },
        {
          label: "Test5",
          link: "/test5",
          icon: <ListIcon />,
        },
      ],
    },
    {
      id: 5,
      label: "Test6",
      link: "/test6",
      icon: <DescriptionIcon />,
    },
    {
      id: 6,
      label: "Test7",
      link: "/test7",
      icon: <AccountBalanceWallet />,
      children: [
        {
          label: "Test8",
          link: "/test8",
          icon: <FaceIcon />,
        },
        {
          label: "Test9",
          link: "/test9",
          icon: <TransferWithinAStationIcon />,
        },
        {
          label: "Test10",
          link: "/test10",
          icon: (
            <Avatar alt="Pesonet" src={pesonetlogo} className={classes.small} />
          ),
        },
        {
          label: "Test11",
          link: "/test11",
          icon: <PeopleAlt />,
        },
      ],
    },
    {
      id: 7,
      label: "Test12",
      link: "/test12",
      icon: <EditIcon />,
    },
  ];

  useEffect(function() {
    window.addEventListener("resize", handleWindowWidthChange);
    handleWindowWidthChange();
    return function cleanup() {
      window.removeEventListener("resize", handleWindowWidthChange);
    };
  });

  return (
    <Drawer
      variant={isPermanent ? "permanent" : "temporary"}
      className={classNames(classes.drawer, {
        [classes.drawerOpen]: isSidebarOpened,
        [classes.drawerClose]: !isSidebarOpened,
      })}
      classes={{
        paper: classNames({
          [classes.drawerOpen]: isSidebarOpened,
          [classes.drawerClose]: !isSidebarOpened,
        }),
      }}
      open={isSidebarOpened}
    >
      <div className={classes.toolbar} />
      <div className={classes.mobileBackButton}>
        <IconButton onClick={() => toggleSidebar(layoutDispatch)}>
          <ArrowBackIcon
            classes={{
              root: classNames(classes.headerIcon, classes.headerIconCollapse),
            }}
          />
        </IconButton>
      </div>
      <List className={classes.sidebarList}>
        {structureNew.map((link) => (
          <SidebarLink
            key={link.id}
            location={location}
            isSidebarOpened={isSidebarOpened}
            {...link}
          />
        ))}
      </List>
    </Drawer>
  );

  function handleWindowWidthChange() {
    var windowWidth = window.innerWidth;
    var breakpointWidth = theme.breakpoints.values.md;
    var isSmallScreen = windowWidth < breakpointWidth;

    if (isSmallScreen && isPermanent) {
      setPermanent(false);
    } else if (!isSmallScreen && !isPermanent) {
      setPermanent(true);
    }
  }
}

export default withRouter(Sidebar);

如您所见,structureNew包含经过一些数组操作在useEffect中形成的菜单和子菜单

但是,我的问题是页面首先被渲染并调用structureNew。此时,structureNew为空

我希望在呈现页面之前先形成structureNew

有什么办法可以使用钩子吗?

TIA

2 个答案:

答案 0 :(得分:1)

为什么不创建一个structureNew作为State并在该useEffect中设置setStructureNew?

答案 1 :(得分:1)

我建议添加一个加载器,例如:

const [loading, setLoading] = useState(true)

然后在进行操作后将加载设置为false:

setLoading(false)

并且在添加返回之前:

if(loading){
    return(<LoaderComponent/>)
}
return(
//your main return
)