Home > database >  React-native: conditional rendering with loginState
React-native: conditional rendering with loginState

Time:03-12

I am trying to create a login for a React Native app.
But the conditional rendering is not working, really don't know what I am doing wrong. (I am new to react native)

The isLoggedIn state checks if the user has a valid refreshToken. The token class works only thing that isn't working is the rendering after logging in.

InsightsApiDriver

  isValidRefreshToken() {
    return (
      this.refreshToken !== undefined &&
      new Date().getTime() - this.refreshToken.datetime.getTime() <
        1 * 60 * 1000
    );
  }

MainContainer

  const [isLoading, setIsLoading] = useState(true);
  const [isLoggedIn, setIsLoggedIn] = useState(undefined);
  useEffect(() => {
    SecureStore.getItemAsync("refreshToken")
      .then((tokenJson) => (tokenJson ? JSON.parse(tokenJson) : undefined))
      .then((tokenObj) => {
        if (tokenObj !== undefined) {
          tokenObj.datetime = new Date(tokenObj.datetime);
        }
        global.AppInsightDriver = new InsightsApiDriver(tokenObj);
        setIsLoading(false);
        setIsLoggedIn(global.AppInsightDriver.isValidRefreshToken());
      });
  }, []);
  return (
    <NavigationContainer>
      {console.log(isLoggedIn)}
      {isLoggedIn ? (
        <Tab.Navigator
          initialRouteName={routes.session}
          screenOptions={({ route }) => ({
            tabBarIcon: ({ focused, color, size }) => {
              let iconName;
              switch (route.name) {
                case routes.session:
                  iconName = focused ? "speedometer" : "speedometer-outline";
                  break;
                case routes.settings:
                  iconName = focused ? "settings" : "settings-outline";
                  break;
                case routes.about:
                  iconName = focused ? "help-circle" : "help-circle-outline";
                  break;
                case routes.login:
                  iconName = focused ? "log-in" : "log-in-outline";
                default:
                  iconName = focused ? "log-in" : "log-in-outline";
                  break;
              }
              return <Icon name={iconName} size={size} color={color} />;
            },
            tabBarActiveTintColor: "#FFC62C",
            tabBarInactiveTintColor: "white",
            headerStyle: { backgroundColor: "#1F2341" },
            headerTitleAlign: "center",
            headerTintColor: "#FFC62C",
            headerTitle: () => (
              <HeaderTitle
                SmartgridoneLogo={SmartgridoneLogo}
                routeName={route.name}
              />
            ),
            tabBarStyle: {
              backgroundColor: "#1F2341",
            },
          })}
        >
          <Tab.Screen name={routes.session} component={SessionScreen} />
          <Tab.Screen name={routes.settings} component={SettingScreen} />
          <Tab.Screen name={routes.about} component={AboutScreen} />
        </Tab.Navigator>
      ) : (
        <Tab.Navigator
          initialRouteName={routes.login}
          screenOptions={({ route }) => ({
            tabBarIcon: ({ focused, color, size }) => {
              let iconName;
              switch (route.name) {
                case routes.session:
                  iconName = focused ? "speedometer" : "speedometer-outline";
                  break;
                case routes.settings:
                  iconName = focused ? "settings" : "settings-outline";
                  break;
                case routes.about:
                  iconName = focused ? "help-circle" : "help-circle-outline";
                  break;
                case routes.login:
                  iconName = focused ? "log-in" : "log-in-outline";
                default:
                  iconName = focused ? "log-in" : "log-in-outline";
                  break;
              }
              return <Icon name={iconName} size={size} color={color} />;
            },
            tabBarActiveTintColor: "#FFC62C",
            tabBarInactiveTintColor: "white",
            headerStyle: { backgroundColor: "#1F2341" },
            headerTitleAlign: "center",
            headerTintColor: "#FFC62C",
            headerTitle: () => (
              <HeaderTitle
                SmartgridoneLogo={SmartgridoneLogo}
                routeName={route.name}
              />
            ),
            tabBarStyle: {
              backgroundColor: "#1F2341",
            },
          })}
        >
          <Tab.Screen name={routes.login} component={LoginScreen} />
        </Tab.Navigator>
      )}
    </NavigationContainer>
  );

CodePudding user response:

The useEffect hook which you're using is more or less acting like a componentDidMount method. Which means it only runs when the screen is first mounted. Initially when you run the app, the isLoggedIn state gets set as false. But after you're logged in, this state is not being updated or atleast you haven't mentioned this in the code. If you also mention the routes.login method then it'll be easier to debug. Second time when you refresh app, the mount method sets the isLoggedIn state as true because the token is valid. So there's your issue

CodePudding user response:

You need to render the whole navigation container when user re-authenticated by adding isLoggedIn on useEffect dependencies array


useEffect(() => {
    SecureStore.getItemAsync("refreshToken")
      .then((tokenJson) => (tokenJson ? JSON.parse(tokenJson) : undefined))
      .then((tokenObj) => {
        if (tokenObj !== undefined) {
          tokenObj.datetime = new Date(tokenObj.datetime);
        }
        global.AppInsightDriver = new InsightsApiDriver(tokenObj);
        setIsLoading(false);
        setIsLoggedIn(global.AppInsightDriver.isValidRefreshToken());
      });
  }, [isLoggedIn]);

CodePudding user response:

SecureStore doesnt provides an event listener for value changes so whenever refreshToken changes you need to recalculate the value of isLoggedIn. That would mean prop drilling setIsLoggedIn down to every component where the token is changed, or opting to use some form of state management. (React.useContext is nice)

With that in mind you will be using the following code at least twice, so you might as well put it in its own file (let's call it updateLogin.js), and export it as a function

updateLogin.js

// pass setIsLoggedIn as a parameter
export default updateIsLoggedIn=setIsLoggedIn=>{
  SecureStore.getItemAsync("refreshToken")
      .then((tokenJson) => (tokenJson ? JSON.parse(tokenJson) : undefined))
      .then((tokenObj) => {
        if (tokenObj !== undefined) {
          tokenObj.datetime = new Date(tokenObj.datetime);
        }
        global.AppInsightDriver = new InsightsApiDriver(tokenObj);
        setIsLoading(false);
        setIsLoggedIn(global.AppInsightDriver.isValidRefreshToken());
      });
}

Now MainContainer becomes:

import updateLogin from '../helpers/updateLogin';
/*
.
.
.
*/
const [isLoading, setIsLoading] = useState(true);
const [isLoggedIn, setIsLoggedIn] = useState(undefined);
useEffect(() => {
  updateLogin(setIsLoggedIn);
}, []);

Then in your LoginScreen in whatever function where you write refreshToken to SecureStore after the write is finished just called updateIsLoggedIn(setIsLoggedIn).

Dont forget to either prop drill setIsLoggedIn.

  • Related