I am trying to show the license agreement screen only once. On that screen, there is an ACCEPT
button, once clicked - it saved a value to AsyncStorage
.
In app.js I then check the value of that key in AsyncStorage
and act accordingly.
My problem is that the Login screen keeps showing first when I expect the License agreement to.
I think it's an async issue but not sure how to resolve it.
I did try initially set loading
to true
in the useState
, and this method works.
But once I accept the agreement the first time and then reload the app - I see the license agreement screen for a quick second before it redirects to the login screen - not ideal.
The console log prints the value as expected, but I think the navigation renders before useEffect logic is complete.
App.js
function App() {
const [loading, setLoading] = useState(false);
useEffect(() => {
load();
}, []);
const load = async () => {
await AsyncStorage.getItem('isAgreementAccepted').then(result => {
console.log('RES: ', result)
if (result == null) {
setLoading(true);
} else {
setLoading(false);
}
})
}
return (
<NavigationContainer>
<Stack.Navigator>
{loading && <Stack.Screen
name="EndUserAgreement"
component={EndUserAgreementScreen}
options={{ headerShown: false }}
/>}
<Stack.Screen
name="Login"
component={LoginScreen}
options={{ headerShown: false }}
/>
CodePudding user response:
like you say, you're Stack.screens are rendering before the loading state is set, so you're login screen loads on it's own, then once loading is set to true your endUserAgreement screen is loading in - but your navigation (react-navigation?) won't automatically switch your route
you can:
navigate to the endUserAgreement screen using the
useNavigation
hook & .navigate() (render bothStack.screen
s in this scenario, and set theinitialRouteName
on theStack.Navigator
to 'login')leave only the
endUserAgreement
screen in place if the storage call returns null, which will force the navigation to only show that screen - something like:
{loading ? (
<Stack.Screen
name="EndUserAgreement"
component={EndUserAgreementScreen}
options={{ headerShown: false }}
/>
) : (
<Stack.Screen
name="Login"
component={LoginScreen}
options={{ headerShown: false }}
/>
{/* ... other screens here */}
)}
- show a loading screen until your storage call is complete, then set the initialRouteName based on it's outcome:
const [loading, setLoading] = useState(null)
const load = async () => {
await AsyncStorage.getItem('isAgreementAccepted').then(result => {
console.log('RES: ', result)
if (result == null) {
setLoading(true);
} else {
setLoading(false);
}
})
}
return (
<NavigationContainer>
{loading === null ? (
<LoadingScreen />
) : (
<Stack.Navigator initialRouteName={loading ? 'EndUserAgreement' : 'login'}>
<Stack.Screen
name="EndUserAgreement"
component={EndUserAgreementScreen}
options={{ headerShown: false }}
/>}
<Stack.Screen
name="Login"
component={LoginScreen}
options={{ headerShown: false }}
/>
so we're defaulting to null
loading state, showing a loading component until it's set to true or false, then setting the initial route name to the correct screen name.
CodePudding user response:
What you need to do here is render the screen normally inside Stack.Navigator
.
And once you get a positive value to know that it is the first time the user opens the application, you must navigate to that screen normally, as shown in the following code:
import { useNavigation } from '@react-navigation/native';
function App() {
const [loading, setLoading] = useState(false);
const navigation = useNavigation();
useEffect(() => {
load();
}, []);
const load = async () => {
await AsyncStorage.getItem('isAgreementAccepted').then(result => {
console.log('RES: ', result)
if (result == null) {
navigation.navigate('EndUserAgreement');
await setLocalStoreData('isAgreementAccepted', 'true');
}
});
}
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="EndUserAgreement"
component={EndUserAgreementScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{ headerShown: false }}
/>