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
.