iamjpcbau
iamjpcbau

Reputation: 404

React.js: How to run useEffect first before rendering page?

I'm trying to implement a dynamic menu which changes its structure depending on the user role

See full code below:

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);

As you can see, structureNew contains the menus and submenus which is being formed in useEffect after some array manipulations

However, my problem is the page gets rendered first and calls structureNew. At this point, structureNew is empty

I would like to have structureNew formed first before rendering the page

Is there any way I can do this using hooks?

TIA

Upvotes: 0

Views: 516

Answers (2)

Halim
Halim

Reputation: 278

I recommend adding a loader for example:

const [loading, setLoading] = useState(true)

then after doing your manipulation set loading to false:

setLoading(false)

and before your add return:

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

Upvotes: 1

mcarn
mcarn

Reputation: 76

Why don't you create a structureNew as a State and you setStructureNew in that useEffect?

Upvotes: 1

Related Questions