How can I update my state before rendering render method - javascript

I simply want my render method code run after update state in useEffect.
I store my token in asyncStorage and now I want if token exist then it redirect at homePage else return at loginPage. But problem is that render method run before updating my state in useEffect. And it keeps me on loginPage even I had token.
My code Is below:
import React, { useEffect, useState } from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import { StyleSheet, Text, View} from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { useLinkProps, useNavigation, CommonActions } from '#react-navigation/native';
import MainScreen from './src/screens/MainScreen';
import SignIn from './src/screens/SignIn';
const Stack = createStackNavigator();
const App = props => {
const [isSigned, setIsSigned] = useState(false);
useEffect(async () => {
let token = await AsyncStorage.getItem("#token");
console.log("token", token);
if (token) {
setIsSigned(true);
props.navigation.navigate('MainScreen');
}
else {
setIsSigned(false);
props.navigation.navigate('SignIn');
}
}, [])
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName={isSigned ? "MainScreen" : "SignIn"}
screenOptions={{
headerShown: false
}}
>
<Stack.Screen name="Login" component={SignIn} />
<Stack.Screen name="MainScreen" component={MainScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
const styles = StyleSheet.create({
container: {
marginTop: Platform.OS === 'android' ? '3%' : '10%'
}
});
export default App;

You could add another state value to conditionally render your markup:
import React, { useEffect, useState } from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import { StyleSheet, Text, View} from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { useLinkProps, useNavigation, CommonActions } from '#react-navigation/native';
import MainScreen from './src/screens/MainScreen';
import SignIn from './src/screens/SignIn';
const Stack = createStackNavigator();
const App = props => {
// Manage state whether component has been loaded.
const [isLoaded, setIsLoaded] = useState(false);
const [isSigned, setIsSigned] = useState(false);
useEffect(() => {
let token = await AsyncStorage.getItem("#token");
if (token) {
setIsSigned(true);
props.navigation.navigate('MainScreen');
} else {
setIsSigned(false);
props.navigation.navigate('SignIn');
}
// Tell your component it's been loaded.
setIsLoaded(true);
}, []);
// Don't render if not loaded.
if (!isLoaded) return <></>;
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName={isSigned ? "MainScreen" : "SignIn"}
screenOptions={{
headerShown: false
}}
>
<Stack.Screen name="Login" component={SignIn} />
<Stack.Screen name="MainScreen" component={MainScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}

Related

expo app error when creating stack navigator

I keep getting the error when I write this:
const Stack = createStackNavigator();
i have installed everything required for it but it gives me this error
undefined is not an object (evaluating 'Object.keys(routeConfigs)')
Here is my code:
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import {Home} from './screens/homepage/home';
import { useFonts } from 'expo-font';
import { NavigationContainer, StackActions } from '#react-
navigation/native';
import { createStackNavigator } from 'react-navigation-stack';
export default function App() {
function HomeScreen() {
return(
<View>
<Text>Hello</Text>
</View>
)
}
const Stack = createStackNavigator();
let [fontsLoaded] = useFonts({
'Main': require('./assets/century.ttf'),
'Main-Bold': require('./assets/century-bold.ttf')
});
if(!fontsLoaded) {
return <Text>Waiting...........</Text>
}
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen style={styles.container} name="Home" component=
{HomeScreen}/>
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
height:'100%',
backgroundColor:'#141F2B',
},
});
try installing #react-navigation/stack and #react-navigation/native, then try doing this:
import { createStackNavigator } from "#react-navigation/stack";
import { NavigationContainer } from "#react-navigation/native";
const Stack = createStackNavigator();
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
style= {styles.container}
name= "Home"
component= {HomeScreen}
/>
</Stack.Navigator>
</NavigationContainer>

The action 'NAVIGATE' with payload {...} was not handled by any navigator

So I'm building an app where i send notification to the user when there's an update. Once the user receives it, on clicking, I want to navigate to a specific screen in my app.
The problem is, to do that I have to use a navigation reference to be able to access the navigation object. I did read the doc, but once applying it I get the following error:
The action 'NAVIGATE' with payload {"name":"HomeScreen","params":{"userName":"Lucy"}} was not handled by any navigator.
Do you have a screen named 'HomeScreen'?
RootNavigation.js:
import * as React from 'react';
import { createNavigationContainerRef } from '#react-navigation/native';
export const navigationRef = createNavigationContainerRef()
export function navigate(name, params) {
navigationRef.current?.navigate(name, params);
}
}
App.js:
import React, { useState } from "react";
import { StyleSheet, Text, View } from "react-native";
import AppNavigator from "./app/navigation/AppNavigator";
import { NavigationContainer } from "#react-navigation/native";
import {navigationRef} from "./app/navigation/rootNavigation";
export default function App() {
return (
<AuthContext.Provider>
<NavigationContainer ref={navigationRef}>
<AppNavigator />
</NavigationContainer>
</AuthContext.Provider>
);
}
AppNavigator.js:
import React, {useEffect, useContext} from "react";
import HomeScreen from "../screens/Home/HomeScreen";
import DashboardScreen from "../screens/Home/DashboardScreen";
import { createDrawerNavigator } from "#react-navigation/drawer";
import FolderNavigator from "./FolderNavigator";
import * as Notifications from "expo-notifications";
import * as RootNavigation from "./rootNavigation";
import AuthNavigator from "./AuthNavigator";
const Drawer = createDrawerNavigator();
const AppNavigator = ({}) => {
useEffect(() =>{
registerForPushNotifications()
Notifications.addNotificationReceivedListener(notification =>{
RootNavigation.navigate('HomeScreen', { userName: 'Lucy' });
})
},[])
const registerForPushNotifications = async ( ) => {
try {
const permissions = await Notifications.getPermissionsAsync();
if (!permissions.granted) return;
/*const token = await Notifications.getExpoPushTokenAsync()*/
const token = (await Notifications.getExpoPushTokenAsync()).data;
/*await expoPushTokenApi.register(token, user.guid)*/
/*console.log(token)*/
} catch (error){
console.log(error)
}
};
return (
<Drawer.Navigator>
<Drawer.Screen
name="Dashboard"
component={DashboardScreen}
/>
<Drawer.Screen
name="Liste des dossiers"
component={FolderNavigator}
/>
<Drawer.Screen
name="Liste des clients"
component={HomeScreen}
/>
</Drawer.Navigator>
)};
export default AppNavigator;
FolderNavigator.js:
import React from "react";
import { createNativeStackNavigator } from "#react-navigation/native-stack";
import HomeScreen from "../screens/Home/HomeScreen";
import FolderDetails from "../screens/FolderDetails";
const Stack = createNativeStackNavigator();
const FolderNavigator = () => (
<Stack.Navigator>
<Stack.Screen
name="Liste des dossiers"
component={HomeScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name="Détails"
component={FolderDetails}
options={{ headerTitle: "", headerBackTitle: "" }}
/>
</Stack.Navigator>
);
export default FolderNavigator;
Directory structure:
This line RootNavigation.navigate('HomeScreen', { userName: 'Lucy' }) is causing the error, because when setting up your navigation, you didn't name any screen HomeScreen, I'm talking about Stack.Screen's name property and Drawer.Screen's name property.

React Native Authentication Navigation - REDUX

I can't change the value of the state of the store using the reducer. I'm making an app which has a login-functionality. When a person opens up the app, depending on the fact if he is logged in or not, it should show the right screen. The problem I'm having right now is it doesn't seem to be able to change the store state out of another screen. Anybody who can help me?
import {createStore} from "redux";
const initialState = {
value: false
}
function reducer(state= initialState, action) {
const newState = {...state};
if(action.type === 'login') {
console.log("hahaha you logged in");
newState.value = true;
}
else if(action.type ==='logout') {
console.log("hahaha you logged out")
newState.value = false;
}
return newState;
}
const store = createStore(reducer);
export default store;
This is the store, this should change the value accordingly.
When the login button is pressed on loginscreen it should call the reducer function.
import React, { useRef, useState } from 'react';
import { StyleSheet, Text, View, TextInput, TouchableOpacity, Image, Dimensions, AsyncStorage } from 'react-native';
import axios from "axios";
import store from "../routes/store"
function LoginScreen({navigation}, props) {
const win = Dimensions.get('window');
const [email,setEmail] = useState('');
const [password, setPassword] = useState('');
const { auth, setAuth } = useAuth();
const [errMsg, setErrMsg] = useState('');
const logInCheck = async (e) => {
console.log("Ingelogd");
store.dispatch({type: 'login'})
}
return(
<Root>
<View style={styles.container}>
<TouchableOpacity style={styles.loginBtn} onPress{logInCheck}>
<Text style={styles.loginText}>LOGIN</Text>
</TouchableOpacity>
</View>
</Root>
)
}
This is the code which should render the right screen depending on the fact if the person is logged in!
import React, { useState, useReducer } from "react";
import { createStore } from 'redux';
import { View,Text } from "react-native";
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import LoginScreen from "../screens/LoginScreen";
import * as SecureStore from 'expo-secure-store';
import axios from "axios";
import Tabs from "./Tabs";
import store from "./store";
import ForgotPasswordScreen from "../screens/ForgotPassword";
const AuthStack = () => {
function rendFunc() {
console.log(store.getState());
return(
<AuthStack.Navigator>
{!store.getState()? (
<AuthStack.Screen name="Tabs" component={Tabs} options={{headerShown : false}}/>
) : (
<>
<AuthStack.Screen
name = "LoginScreen"
component={LoginScreen}
/>
<AuthStack.Screen
name = "ForgotPassword"
component={ForgotPasswordScreen},
/>
</>
)
}
</AuthStack.Navigator>
);
}
return (
rendFunc()
);
store.subscribe(rendFunc);
};
export default AuthStack;
The problem with your code is in react re-render rather than the redux store not updating. The store is updating properly but your react component is not aware of any change that has occurred in the store so no re-render is happening.
Firstly you need to add subscriptions to the redux store listener in useEffect i.e. when the component is mounted and later unsubscribe to prevent memory leakages. The redux subscribe function takes in a function to handle whenever state change has occurred in the redux store.
In this function, you can create a state using useState to create a re-render of the component.
use the below code in the authstack and it should work fine.
if any more queries you can contact me # wizcoderzcorp#gmail.com
import React, { useState, useReducer, useEffect } from "react";
import { createStore } from 'redux';
import { View,Text } from "react-native";
import { createNativeStackNavigator } from '#react-navigation/native-
stack';
import LoginScreen from "../screens/LoginScreen";
import * as SecureStore from 'expo-secure-store';
import axios from "axios";
import Tabs from "./Tabs";
import store from "./store";
import ForgotPasswordScreen from "../screens/ForgotPassword";
const AuthStack = () => {
const [loginState, setLoginState] = useState(false)
const handleReduxStateChange = () =>{
setLoginState(store.getState().value)
}
useEffect(()=>{
const unsubscribe = store.subscribe(handleReduxStateChange);
return unsubscribe()
},[])
function rendFunc() {
console.log(store.getState());
return(
<AuthStack.Navigator>
{!store.getState().value? (
<AuthStack.Screen name="Tabs" component={Tabs} options=
{{headerShown : false}}/>
) : (
<>
<AuthStack.Screen
name = "LoginScreen"
component={LoginScreen}
/>
<AuthStack.Screen
name = "ForgotPassword"
component={ForgotPasswordScreen},
/>
</>
)
}
</AuthStack.Navigator>
);
}
return (
rendFunc()
);
};
export default AuthStack;

React Native and firebase Authentication navigation

I am trying to do user authentication using Firebase + Expo.
Currently working on the Authentication flows, but I keep getting this error that i can't seem to fix :(
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import {login} from './login';
import {pageOne} from './pageOne';
import {pageTwo} from './pageTwo';
import 'react-native-gesture-handler';
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import {firebase} from './fbConfig';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
export default function App() {
return (
firebase.auth().onAuthStateChanged((authenticate)=>{
if (authenticate) {
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name="PageOne"
component={pageOne}
/>
<Tab.Screen
name="PageTwo"
component={pageTwo}
/>
</Tab.Navigator>
</NavigationContainer>
} else
{
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Login"
component={login}
/>
</Stack.Navigator>
</NavigationContainer>
}
})
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Its currently showing an error:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
It is because the React is expecting a component to be returned from the App function but here you are returning the firebase.auth().onAuthStateChanged() which ultimately does not return any component. It(onAuthStateChanged) helps to register a listener for user's auth state change and returns a method to unsubscribe from the firebase auth listener on the component unmount(or whenever you would like to remove the auth state listener).
You can check this link for more details.
In your example, you can do it something like:
import React, {useState} from 'react';
import { StyleSheet} from 'react-native';
import { login } from './login';
import { pageOne } from './pageOne';
import { pageTwo } from './pageTwo';
import 'react-native-gesture-handler';
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { firebase } from './fbConfig';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
export default function App() {
const [initializing, setInitializing] = useState(true);
const [user, setUser] = useState();
function onAuthStateChanged(user) {
setUser(user);
if (initializing) setInitializing(false);
}
useEffect(() => {
const subscriber = firebase.auth().onAuthStateChanged(onAuthStateChanged);
return subscriber; // unsubscribe on unmount
}, []);
if (initializing) return null;
if (!user) {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Login"
component={login}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name="PageOne"
component={pageOne}
/>
<Tab.Screen
name="PageTwo"
component={pageTwo}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});

currentUser is returning undefined

I'm new to programming and following this video, my code until now has had no problems, but when I try
console.log(currentUser);
It returns undefined while it shouldn't, the user is logged in and all of their information is correct in Firebase. Also, my code doesn't show any errors.
This is the code:
import React, { Component } from 'react';
import {Text, View} from 'react-native';
import firebase from 'firebase'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchUser } from '../../redux/actions/index';
export class Main extends Component {
componentDidMount() {
this.props.fetchUser();
}
render() {
const { currentUser } = this.props;
console.log(currentUser)
if (currentUser==undefined){
return(
<View></View>)
}
return (
<View style={{ flex: 1, justifyContent: 'center'}}>
<Text>User is logged in</Text>
</View>
)
}
}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser
})
const mapDispatchProps = (dispatch) => bindActionCreators({fetchUser}, dispatch)
export default connect(null, mapDispatchProps)(Main);
And this is the parent component code:
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
require('dotenv').config();
import firebase from 'firebase';
const firebaseConfig = {
//personal info
};
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './redux/reducers';
import thunk from 'redux-thunk';
const store = createStore(rootReducer, applyMiddleware(thunk))
if(firebase.apps.length === 0){
firebase.initializeApp(firebaseConfig)
}
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import {createMaterialTopTabNavigator} from 'react-navigation-tabs';
import LandingScreen from './Components/auth/Landing';
import RegisterScreen from './Components/auth/Register';
import MainScreen from './Components/auth/Main';
const Stack = createStackNavigator();
export class App extends Component {
constructor(props) {
super(props);
this.state = {
loaded: false,
}
}
componentDidMount(){
firebase.auth().onAuthStateChanged((user) => {
if (!user){
this.setState({
loggedIn: false,
loaded: true,
})
}else{
this.setState({
loggedIn: true,
loaded: true,
})
}
})
}
render() {
const { loggedIn, loaded } = this.state
if (!loaded){
return(
<View style={{ flex: 1, justifyContent: 'center'}}>
<Text>Loading</Text>
</View>
)
}
if (!loggedIn) {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Landing">
<Stack.Screen name="Landing" component={LandingScreen} options={{headerShown: false}}/>
<Stack.Screen name="Register" component={RegisterScreen}/>
</Stack.Navigator>
</NavigationContainer>
);
}
return(
<Provider store={store}>
<MainScreen/>
</Provider>
)
}
};
export default App

Categories