asyncstorage not working in build mode in react native - react-native

in debug mode asyncstorage working perfectly fine but when build apk with this command
gradlew assembleRelease -x bundleReleaseJsAndAssets
the apk build perfectly but i want to open it show me the error appstop in my phone
any help regarding this will be very appriciated

Try this.
import AsyncStorage from '#react-native-async-storage/async-storage';
export default function App()
{
const [aldreadyLaunched, setaldreadyLaunched] = useState(true)
useEffect(() => {
AsyncStorage.getItem("alreadyLaunched").then((value) => {
if (value == "false") {
let parsedValue = JSON.parse(value)
setaldreadyLaunched(parsedValue)
}
})
},[])
return (<>
display introductory screen when the already launched state is true and when the false display login or directly main screen
</>)
}

Try this .....
const [loading, setLoading] = useState(true);
const [isFirstTimeLoad, setIsFirstTimeLoad] = useState(false);
const checkForFirstTimeLoaded = async () => {
const result = await AsyncStorage.getItem('isFirstTimeOpen');
console.log('result:', result);
if (result == null) {
setIsFirstTimeLoad(true);
setLoading(false);
} else {
setIsFirstTimeLoad(false);
setLoading(false);
}
};
if (loading)
return (
<View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
<ActivityIndicator size={'large'} color={'black'}/>
</View>
);
if (isFirstTimeLoad)
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="OnboardingScreen"
screenOptions={{
headerShown: false,
// header: () => <MainHeader />,
}}>
<Stack.Screen name="OnboardingScreen" component={OnBoardingScreen} />
<Stack.Screen name="login" component={Login} />
<Stack.Screen name="home" component={Home} />
<Stack.Screen name="register" component={Register} />
<Stack.Screen name="mobileverify" component={MobileVerify} />
<Stack.Screen name="listscreen" component={ListData} />
</Stack.Navigator>
</NavigationContainer>
);
if (!isFirstTimeLoad) return <Login />;

Related

Navigate between two stack Navigators after login in react native

This is my first stack
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerShown: false,
}}
>
<Stack.Screen name="Home" component={MainScreen} />
<Stack.Screen name="Schools" component={SchoolsScreen} />
<Stack.Screen name="Setting" component={SettingScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen name="Saved" component={SavedData} />
<Stack.Screen name="Profile2" component={Profile2} />
<Stack.Screen name="SchoolDetails" component={SchoolDetailsScreen} />
<Stack.Screen name="Bottom" component={BottomTab} />
</Stack.Navigator>
</NavigationContainer>
This is my second stack
<NavigationContainer>
<Stack.Navigator
initialRouteName="Login"
screenOptions={{
headerShown: false,
}}
>
<Stack.Screen name="Login" component={AuthLogin} />
<Stack.Screen name="Register" component={AuthRegister} />
<Stack.Screen name="Forget" component={ForgetPassword} />
</Stack.Navigator>
</NavigationContainer>
THis is my app.js file
const [auth, setAuth] = useState(false);
useEffect(() => {
(async () => {
const value = await AsyncStorage.getItem("isLoggedin");
console.log(value);
setAuth(value);
})();
}, []);
return (
<SafeAreaView style={{ flex: 1 }}>
<NativeBaseProvider config={config}>
{auth == "true" ? <InsideStack /> : <OutsideStack />}
</NativeBaseProvider>
</SafeAreaView>
);
}
I want to navigate to home screen from login screen , after function call
// navigation.push("Home");
const storeData = async (value) => {
try {
await AsyncStorage.setItem("isLoggedin", JSON.stringify(true));
} catch (e) {
// saving error
}
};
storeData();
navigation.push("Home");
}}
But i got error:
Do you have a screen named 'Home'?
this is the simple code for this. you have to use redux or context api for gloal state management. here is a min example ;
import {NavigationContainer} from '#react-navigation/native';
import React from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {GetUserContacts} from '../logic/getcontact';
import {setContacts} from '../store/actions/ContactActions';
import AuthNavigation from './AuthNavigation';
import RootNavigation from './RootNavigation';
const AppContainer = () => {
const {is_logged_in} = useSelector(state => state.persistedReducer);
const dispatch = useDispatch();
if (is_logged_in) {
GetUserContacts('PK').then(data => {
dispatch(setContacts(data));
});
}
return (
<NavigationContainer>
{is_logged_in ? <RootNavigation /> : <AuthNavigation />}
</NavigationContainer>
);
};
export default AppContainer;
after user logged in you have to make that is_logged_in true. so the navigator changes itself. you will have to persist this so when application restart the user dont need to login again

How to navigate to other screen after the whole app has loaded?

Good day! Currently learning Nav 5 and so far this is what I have tried. I want to navigate to other screens depending on the result of the Firebase login check.
But what happens is the app renders 3times in the Splash screen, what I want is the 3rd or the final data. How can I know when the app is done re-rendering stuff.
This is what I have tried and I am fairly new to React navigation hopefully somebody could point me on how to do this properly. Thank you!
APP.JS
useEffect(() => {
const subscriber = firebase.auth().onAuthStateChanged(user => {
let logged = false
if (user) {
logged = true
firebase.firestore().collection('users')
.doc(user.uid)
.get()
.then(firestoreDocument => {
if (!firestoreDocument.exists) {
return
}
const user = firestoreDocument.data()
console.log("subs data", user)
setUser(user)
})
.catch(error => {
alert(error)
})
} else {
setUser(null)
logged = false
}
setIsLoggedIn(logged)
setIsLogIncheckDone(true)
})
return subscriber // unsubscribe on unmount
}, [])
const getSomething = async () => {
const data = {
isLoggedIn, user, isLogInCheckDone
}
return data
}
if (!isLogInCheckDone) {
return <SplashScreen />
}
return (
<AppContext.Provider value={{ GetSomething: getSomething }}>
<ThemeContextProvider>
<ThemeContextConsumer>
{context =>
(
<NavigationContainer>
<Stack.Navigator theme={context.theme ? "dark" : "light"} >
<Stack.Screen name={"Splash"} component={SplashScreen} options={{ headerShown: false }} />
<Stack.Screen name={"Common"} component={CommonStackNav} options={{ headerShown: false }} />
<Stack.Screen name={"Auth"} component={AuthStackNav} options={{ headerShown: false }} />
<Stack.Screen name={"Dashboard"} component={DashboardStack} options={{ headerShown: false }} />
<Stack.Screen name={"Verify"} component={VerificationStackNav} options={{ headerShown: false }} />
<Stack.Screen name={"Wizard"} component={ProfileWizardStack} options={{ headerShown: false }} />
</Stack.Navigator>
<Toast ref={(ref) => Toast.setRef(ref)} />
</NavigationContainer>
)
}
</ThemeContextConsumer>
</ThemeContextProvider>
</AppContext.Provider>
)
}
SPLASHSCREEN.JS
export default function SplashScreen({ navigation, route }) {
const appContext = useContext(AppContext)
useEffect(() => {
const newData = appContext?.GetSomething()
console.log("Splashy", newData)
//TODO Navigate here
}, [appContext])
return (
<View style={styles.container}>
<LottieView source={newSource} autoPlay loop />
</View>
)
}
You can use the logged state in your app.js to render what you want. Try something like this:
return (
<NavigationContainer >
{logged ? <Screens you want to render if the user is logged /> : <SplashScreen />}
</NavigationContainer>
);

React native wait until user is logged in before navigating screens

I have a function that retrieves a token from SecureStore in react-native
export const isUserLoggedIn = async () =>{
return await SecureStore.getItemAsync('jwtToken') ? true : false
}
This is my navigator:
function RootNavigator() {
const [isLoggedIn, setIsLoggedIn] = useState(false)
console.log("isUserLoggedIn()", isUserLoggedIn());
(async () => {
const someBoolean = await isUserLoggedIn()
console.log("inside then")
setIsLoggedIn(someBoolean)
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
{isLoggedIn || <Stack.Screen name="Root" component={WelcomeScreen} />}
{isLoggedIn && <Stack.Screen name="InvestorProfileQuiz" component={InvestorProfileQuizScreen} />}
{isLoggedIn || <Stack.Screen name="AppTour" component={AppTourScreen} />}
{isLoggedIn || <Stack.Screen name="Login" component={LoginScreen} />}
{isLoggedIn || <Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title: 'Oops!' }} />}
</Stack.Navigator>
);
})()
console.log("LOGGED IN STATE:", isLoggedIn)
The issue is that the LOGGED_IN_STATE is logged before the inside then console.log which means the code isn't blocking and waiting for the isUserLoggedIn() to resolve. isUserLoggedIn() returns a promise because it's an async await function, but do I wait for it to resolve before rendering the Stack Navigator? I in short want a logged in user to have access to certain screens and not others. What am I doing wrong?
I usually handle logged in state with something like this:
function RootNavigator() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
// Passing an empty array as second argument
// makes useEffect behave like componentDidMount
useEffect(() => {
// Wrap logic in the async init function
// since the useEffect callback can't be async
async function init() {
const someBoolean = await isUserLoggedIn();
setIsLoggedIn(someBoolean);
}
init();
}, []);
// Return null or your logged out screen component here.
if (!isLoggedIn) {
return null;
}
return (
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name="Root" component={WelcomeScreen} />}
<Stack.Screen
name="InvestorProfileQuiz"
component={InvestorProfileQuizScreen}
/>
<Stack.Screen name="AppTour" component={AppTourScreen} />
<Stack.Screen name="Login" component={LoginScreen} />(
<Stack.Screen
name="NotFound"
component={NotFoundScreen}
options={{title: 'Oops!'}}
/>
</Stack.Navigator>
);
}
In your code console.log("LOGGED IN STATE:", isLoggedIn) does not wait because the async await is incapsulated in the IIFE (https://developer.mozilla.org/en-US/docs/Glossary/IIFE) function scope.

React navigation 5 stack navigator and drawer navigator together

Basically I am from react-navigation v4, I am familiar handling the same with SwitchNavigator as bridge, but v5 does no support for SwitchNavigator, so bit struggling to understand the below implementation.
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
function AuthenticationStack({ navigation }) {
return (
<Stack.Navigator initialRouteName="Home" screenOptions={{headerShown: false}}>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Registration" component={Registration} />
</Stack.Navigator>
);
}
function UserStack({ navigation }) {
return (
<Stack.Navigator initialRouteName="Jobs" screenOptions={{headerShown: false}}>
<Stack.Screen name="Jobs" component={Profile} />
<Stack.Screen name="JobDetails" component={JobDetails} />
</Stack.Navigator>
);
}
function App() {
return (
<NavigationContainer>
<Drawer.Navigator drawerContent={props => <SideBar { ...props } />}>
<Drawer.Screen name="Home" component={AuthenticationStack} />
<Drawer.Screen name="Jobs" component={UserStack} />
</Drawer.Navigator>
</NavigationContainer>
);
}
export default App;
The above code works but have some problems like,
While loading the app, initially the drawer is visible and goes hidden when the app loaded.
I do not want to have drawers for Authentication screens, If I have 2 different navigation without splitting AuthenticationStack and UserStack then I am facing problem while navigating from Login to Profile and vice versa
UPDATE 1
export default() => {
const [logged, setUser] = React.useState(false);
return (
<NavigationContainer>
{
logged
?
<DrawerScreen initialParams={{ setUser }} />
:
<AuthStackScreen initialParams={{ setUser }} />
}
</NavigationContainer>
);
}
Now in login.js, I need to update setUser to true, right? If yes how can I access setUser in my login.js file
Update 2
class Login extends Component {
fetch(login_url, {
method: "POST",
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Accept-Language': 'en-US' },
body: JSON.stringify(data)
})
.then((response) => {
const success = response.ok;
const data = response.json();
return Promise.all([success, data]);
})
.then(([success, response]) => {
var data, userInfo;
if (success) {
data = {
token: response.token,
user: response.user,
}
if(_storeUserData(response)) {
_retrieveUserData().then((userInfo) => {
this.setState({
logged: true,
userInfo: userInfo,
loading:false,
email: '',
password: ''
});
navigate('Profile');
})
.catch((error) => {
// alert(error);
});
}
}
this.setState(state_data);
})
.catch((error) => {
alert('Error:'+error);
this.setState({ loading: false});
});
}
You can create something similar to the switch navigator by conditionally rendering navigators like below.
export default function App() {
const [loggedIn, setLoggedIn] = React.useState(false);
return (
<NavigationContainer>
{loggedIn ? (
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} initialParams={{ setLoggedIn }}/>
<Drawer.Screen name="Notifications" component={NotificationsScreen} />
</Drawer.Navigator>
) : (
<Stack.Navigator>
<Stack.Screen
name="Auth"
component={AuthScreen}
initialParams={{ setLoggedIn }}
/>
</Stack.Navigator>
)}
</NavigationContainer>
);
}
You can tryout this snack for a working example
https://snack.expo.io/#guruparan/switchsample

Navigating between different stackscreen in react-navigation 5

isSignedIn ? (
<>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</>
) : (
<>
<Stack.Screen name="SignIn" component={SignInScreen} />
<Stack.Screen name="SignUp" component={SignUpScreen} />
</>
)
How can I navigate from signInScreen to HomeScreen.
You can make a context to handle the authentification flow and on your app launch check if a token or anything is already set.
const Index = () => {
const [authState, dispatch] = useAuthContext();
useEffect(() => {
const boostrapAsync = async () => {
let userToken = null;
try {
userToken = await AsyncStorage.getItem('userToken');
} catch (e) {
console.log('Restoring token failed');
}
restoreToken({ token: userToken }, dispatch);
};
boostrapAsync();
}, []);
if (authState.isLoading) {
return (
<SplashScreen />
);
}
return (
<Root>
<NavigationContainer>
<UIProvider reducer={UIReducer} initialState={initialStateUIReducer}>
{authState.userToken ? (
<UserProvider reducer={UserReducer} initialState={initialStateUserReducer}>
<AppNavigation />
</UserProvider>
) : <AuthNavigation />}
</UIProvider>
</NavigationContainer>
</Root>
);
};
export default Index;
These problems can be solved with a splash screen. I think you should collect all screens under one tag. Then, make a splash screen. After that, you can control the parameter is called isSignedIn in the SplashScreen. Then you navigate the screen which you want.