reactjsionic-frameworkreact-propsreact-stateside-menu

How to update side menu item list in an ionic react app after login


I'm creating a mobile app with ionic react and used the "ionic start --sidemenu" option. This is my App.tsx

<IonApp>
      <IonReactRouter>
        <IonSplitPane contentId="main">
          <Menu isLogged={isLoggedIn} userType={userType} logoutFunc={logout} />
          <IonRouterOutlet id="main">
            <Route path="/user/auth" render={(props) => isLoggedIn ? <Forum {...props} isLoggedIn={isLoggedIn} /> : <AuthHome {...props} />} exact />
            <Route path="/user/register" render={(props) => isLoggedIn ? <Forum {...props} isLoggedIn={isLoggedIn} /> : <Register {...props} />} exact />
            <Route path="/user/register-as-owner" render={(props) => isLoggedIn ? <Forum {...props} isLoggedIn={isLoggedIn} /> : <RegisterAsOwner {...props} />} exact />
            <Route path="/user/login" render={(props) => isLoggedIn ? <Forum {...props} isLoggedIn={isLoggedIn} /> : <Login {...props} />} exact />
            <Route path="/user/profile" render={(props) => <Profile {...props} />} exact />
            <Route path="/user/inbox" render={(props) => <Inbox />} exact />
            <Route path="/user/inbox/:id" render={(props) => <Mail {...props} />} exact />
            <Route path="/user/profile/my-notification" component={MyNotification} exact />
            <Route path='/user/create-team' render={(props) => <CreateTeam {...props} />} exact />
            <Route path='/list-teams' render={props => <TeamListing {...props} />} exact />
            <Route path='/team/:id' render={props => <Team {...props} />} exact />
            <Route path="/forum" component={Forum} exact />
            <Route path="/game" render={(props) => <CreateGameNotification {...props} />} exact />
            <Route path="/game/:id" render={(props) => <GameInfo {...props} />} exact />
            <Route path="/game/comments/:id" render={(props) => <GameComments {...props} />} exact />
            <Redirect from="/" to={isLoggedIn ? "/forum" : "/user/auth"} exact />
            <Redirect from="/user/logout" to="/user/auth" exact />
          </IonRouterOutlet>
        </IonSplitPane>
      </IonReactRouter>
    </IonApp>

And this is the Menu.tsx component

const getMenuItem = (appPage: AppPage, index: number) => {
    return (
      <IonMenuToggle key={appPage + "-" + index} autoHide={false}>
        <IonItem className={location.pathname === appPage.url ? 'selected' : ''} routerLink={appPage.url} routerDirection="none" lines="none" detail={false}>
          <IonIcon slot="start" ios={appPage.iosIcon} md={appPage.mdIcon} />
          <IonLabel>{appPage.title}</IonLabel>
        </IonItem>
      </IonMenuToggle>
    );
  };

  return (
    <IonMenu contentId="main" type="overlay">
      <IonContent>
        <IonList id="inbox-list">
          <IonListHeader>Menu</IonListHeader>
          {props.isLogged
            ? props.userType == "user"
              ? menuItem.UserMenuPage.map((page: AppPage, i) => getMenuItem(page, i))
              : props.userType == "owner"
                ? menuItem.OwnerMenuPage.map((page: AppPage, i) => getMenuItem(page, i))
                : props.userType == "admin"
                  ? menuItem.AdminMenuPage.map((page: AppPage, i) => getMenuItem(page, i))
                  : <></>
            : menuItem.UnauthenticatedUserMenuPage.map((page: AppPage, i) => getMenuItem(page, i))}
          <IonMenuToggle autoHide={false}>
            <IonItem onClick={e => props.logoutFunc(history)} detail={false}>
              <IonIcon slot="start" ios={personSharp} md={personSharp} />
              <IonLabel>Dil nga profili</IonLabel>
            </IonItem>
          </IonMenuToggle>
        </IonList>
      </IonContent>
    </IonMenu>
  );

When the app opens it checks if the user is already logged, if not it redirect to the login. After the login the user is redirected to the forum page.


 const [gameList, setGameList] = useState<Game[]>();
    const [unreadMails, setUnreadMails] = useState<number>(0);

    useIonViewWillEnter(() => {
        fetch(env.game_api)
            .then(res => res.json()
                .then(data => {
                    setGameList(data);
                }))
            .catch(err => {
                console.log(err);
            });

        authservice.getUserIdFromStorage()
        .then(id => {
            fetch(env.mail_api + "/user/unread/" + id.value)
            .then(resp => resp.json())
            .then(res => setUnreadMails(res))
            .catch(err => console.log(err));
        });
    });

<IonPage>
            <IonHeader>
                <IonToolbar>
                    <IonButtons slot="start">
                        <IonMenuButton />
                    </IonButtons>
                    <IonTitle color="success">Njoftime</IonTitle>
                    <IonButton onClick={e => props.history.push("/user/inbox")} slot="end" fill="clear">
                        <IonIcon icon={mailOutline} size="large"></IonIcon>
                        {unreadMails > 0 && <IonLabel>{unreadMails}</IonLabel>}
                    </IonButton>
                </IonToolbar>
            </IonHeader>
            <IonContent fullscreen>
                {gameList ? gameList.map((game, i) => {
                    return (
                        <IonCard key={game.title + "-" + i} >
                            <IonCardHeader>
                                <IonCardTitle>
                                    <IonRouterLink href={"/user/profile?user=" + game.posted_by}>
                                        <IonLabel>{game.posted_by_name}</IonLabel>
                                    </IonRouterLink>
                                </IonCardTitle>
                                <IonCardSubtitle>Shkodra Fc</IonCardSubtitle>
                            </IonCardHeader>
                            <IonItem routerLink={"/game/" + game._id} >
                                <IonIcon icon={footballOutline} slot="start" />
                                <IonLabel>Info Mbi ndeshjen</IonLabel>
                            </IonItem>
                            <IonItem lines="none" routerLink={"/game/comments/" + game._id}>
                                <IonIcon icon={chatboxOutline} slot="start" />
                                <IonLabel>Komente te Tjera</IonLabel>
                            </IonItem>
                            
                        </IonCard>
                    )
                }) : <> </>}
            </IonContent>
</IonPage>

Depending on the user role I want to show different side menu items but after the user successfully logs in the sidemenu items do not update. In what way do I have to change my code to make it work? Thanks in advance!


Solution

  • use a state manager... but for auth, I usually use the Context API to hold authentication state which is then accessible throughout my app.

    The complete solution is here Code from state presentation @ Ioniconf, but stack overflow doesn't like when you just paste links so here is some of the code.

    I have a side menu in the demo app also

    import React from "react";
    
    // create the context
    export type IAuthContext = {
      authInfo: {
        loggedIn: boolean;
        user: {
          email: string;
          id: string;
        };
      };
      logOut: any;
      logIn: any;
    };
    const AuthContext = React.createContext<any>(undefined);
    
    // create the context provider, we are using use state to ensure that
    // we get reactive values from the context...
    export const AuthProvider: React.FC = ({ children }) => {
      // the reactive values
      const [authInfo, setAuthInfo] = React.useState<any>();
    
      const logOut = () => {
        return new Promise((resolve) => {
          setAuthInfo({ loggedIn: false, user: null });
          setTimeout(() => {
            return resolve(true);
          }, 1000);
        });
      };
    
      const logIn = (email: string, password: string) => {
        return new Promise((resolve) => {
          let v = {
            loggedIn: true,
            user: { email, id: new Date().getTime() + "" },
          };
          setAuthInfo(v);
          setTimeout(() => {
            return resolve(true);
          }, 1000);
        });
      };
    
      let v = {
        authInfo,
        logOut: logOut,
        logIn: logIn,
      };
    
      return <AuthContext.Provider value={v}>{children}</AuthContext.Provider>;
    };
    
    export const useAuth = () => React.useContext(AuthContext) as IAuthContext;
    

    Wrap the app with the context, this is my index.tsx

    import { AuthProvider } from "./AuthContext";
    
    ReactDOM.render(
      <AuthProvider>
        <App />
      </AuthProvider>,
      document.getElementById("root")
    );
    

    Now to use it, you import it

    import { useAuth } from "../AuthContext";
    

    Then in your code access, it's methods and properties

    const {authInfo} = useAuth();
    console.log(authInfo.loggedIn)