I'm making a ChatApp on ReactNative, on this part I'm trying to implement a SignOut function, but i don't understand what is wrong here.
enter image description here
The error is "undefined is not a function (near '...navigation.setOptions...')
The error is on the function:
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity style={{ marginRight: 10 }} onPress={onSignOut}>
//<AntDesign name="logout" size={24} color={"#808080"} style={{marginRight:10}}/>
</TouchableOpacity>
)
});
}, [navigation]);
import React, {
useState,
useEffect,
useLayoutEffect,
useCallback,
} from "react";
import { TouchableOpacity, Text } from "react-native";
import { GiftedChat } from "react-native-gifted-chat";
import {
collection,
addDoc,
query,
onSnapshot
} from 'firebase/firestore';
import { signOut } from 'firebase/auth';
import { database } from "../config/firebase";
import { useNavigation } from "#react-navigation/native";
import {AntDesign} from "#expo/vector-icons";
import { Colors } from "react-native/Libraries/NewAppScreen";
import { auth } from "../config/firebase"
export default function Chat() {
const [messages, setMessages] = useState([]);
const navigation = useNavigation();
const onSignOut = () => {
signOut(auth).catch(error => console.log(error));
};
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity style={{ marginRight: 10 }} onPress={onSignOut}>
//<AntDesign name="logout" size={24} color={"#808080"} style={{marginRight:10}}/>
</TouchableOpacity>
)
});
}, [navigation]);
useLayoutEffect(() => {
const collectionRef = collection(database, 'chats');
const q = query(collectionRef, orderBy('createAt', 'desc'));
const unsubscribe = onSnapshot(q, snapshot => {
console.log('snapshot');
setMessages(
snapshot.docs.map(doc => ({
_id: doc.id,
createdAt: doc.data().createdAt,
text: doc.data().text,
user: doc.data().user,
}))
)
});
return () => unsubscribe();
}, []);
const onSend = useCallback((messages = []) => {
setMessages(previousMessages => GiftedChat.append(previousMessages, messages));
const { _id, createdAt, text, user } = messages[0];
addDoc(collection(database, 'chats'), {
_id,
createdAt,
text,
user,
});
}, []);
return (
<GiftedChat
messages={messages}
onSend={messages => onSend(messages)}
user = {{
_id: auth?.currentUser?.email,
avatar: 'https://i.pravatar.cc/300'
}}
/>
);
}
The Navigation is on this part
This is the main part of the code, here i make the navigation into de login and signup part and the chat and home screens.
'''App.js Javascript
import React, {useState, createContext, useContext, useEffect} from "react";
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
import { View, ActivityIndicator } from "react-native";
import { onAuthStateChanged } from "firebase/auth";
import Chat from "./src/screens/Chat";
import Login from "./src/screens/Login";
import Signup from "./src/screens/Signup";
import Home from "./src/screens/Home"
import { auth } from "./src/config/firebase";
const Stack = createStackNavigator();
const AuthenticatedUserContext = createContext({});
const AuthenticatedUserProvider = ({ children }) => {
const [user, setUser] = useState(null);
return (
<AuthenticatedUserContext.Provider value = {{user, setUser}}>
{children}
</AuthenticatedUserContext.Provider>
)
}
function AuthStack () {
return (
<Stack.Navigator defaultScreenOptions={Login} screenOptions={{headerShown: false}}>
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Signup" component={Signup} />
</Stack.Navigator>
)
}
function ChatStack () {
<Stack.Navigator defaultScreenOptions={Home}>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Chat" component={Chat} />
</Stack.Navigator>
}
function RootNavigator () {
const {user, setUser} = useContext(AuthenticatedUserContext);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth,
async authenticatedUser => {
authenticatedUser ? setUser(authenticatedUser) : setUser(null);
setLoading(false);
}
);
return () => unsubscribe();
}, [user]);
if(loading) {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<ActivityIndicator size="large" />
</View>
)
}
return (
<NavigationContainer>
{ user ? <ChatStack /> : <AuthStack />}
</NavigationContainer>
)
}
export default function App(){
return (
<AuthenticatedUserProvider>
<RootNavigator />
</AuthenticatedUserProvider>
)
}
'''
Related
I have implemented AWS custom UI authenctication in my react native app and React navigation to navigate through the different screens.
While implementing the logical conditions to see if the "User is already logged in or not" I have assigned the screen "Home" for logged in user and screen 'Login' for not logged in user it's working fine and navigating as expected but the console is showing this error when clicking on login button.
ERROR The action 'NAVIGATE' with payload {"name":"Home"} was not handled by any navigator.
Here is the Navigation code:
import React, {useEffect, useState} from 'react'
import {ActivityIndicator, Alert, View} from 'react-native';
import HomeScreen from '../screens/HomeScreen';
import LoginScreen from '../screens/LoginScreen';
import RegisterScreen from '../screens/RegisterScreen';
import ConfirmEmailScreen from '../screens/ConfirmEmailScreen';
import ForgotPassword from '../screens/ForgotPassword';
import NewPasswordScreen from '../screens/NewPasswordScreen';
import { NavigationContainer } from '#react-navigation/native'
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import {Auth, Hub} from 'aws-amplify';
const Stack = createNativeStackNavigator();
const Navigation = () => {
const [user, setUser] = useState(undefined);
//Checking if user is already logged in or not!
const checkUser = async () => {
try {
const authUser = await Auth.currentAuthenticatedUser({bypassCache: true});
setUser(authUser);
} catch(e) {
setUser(null);
}
};
useEffect(() => {
checkUser();
}, []);
useEffect(() => {
const listener = data => {
if (data.payload.event === 'signIn' || data.payload.event === 'signOut') {
checkUser();
}
}
Hub.listen('auth', listener);
return () => Hub.remove('auth', listener);
}, []);
if (user === undefined) {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<ActivityIndicator/>
</View>
);
}
return (
<NavigationContainer>
<Stack.Navigator>
{/*{If user is logged in navigate him to Homescreen else go throght the Screens based on the user selection */}
{user ? (
<Stack.Screen name='Home' component={HomeScreen}/>
) : (
<>
<Stack.Screen name='Login' component={LoginScreen}/>
<Stack.Screen name='Register' component={RegisterScreen}/>
<Stack.Screen name='ConfirmEmail' component={ConfirmEmailScreen}/>
<Stack.Screen name='ForgotPassword' component={ForgotPassword}/>
<Stack.Screen name='NewPassword' component={NewPasswordScreen}/>
</>
)}
</Stack.Navigator>
</NavigationContainer>
);
};
export default Navigation;
Here is the Login Screen:
import React, {useState} from 'react'
import { View, Text, Image, StyleSheet, useWindowDimensions, TouchableWithoutFeedback, Keyboard, Alert, KeyboardAvoidingView, ScrollView } from 'react-native'
import Logo from '../../../assets/images/logo-main.png'
import CustomButton from '../../components/CustomButton/CustomButton';
import CustomInput from '../../components/CustomInput/CustomInput';
import { useNavigation } from '#react-navigation/native';
import {Auth} from 'aws-amplify';
import {useForm} from 'react-hook-form';
const LoginScreen = () => {
const [loading, setLoading] = useState(false);
const {height} = useWindowDimensions();
const {control, handleSubmit, formState: {errors}} = useForm();
const navigation = useNavigation();
const onLoginPressed = async (data) => {
if(loading) {
return;
}
setLoading(true);
try {
await Auth.signIn(data.username, data.password);
navigation.navigate('Home');
} catch(e) {
Alert.alert('Opps', e.message)
}
setLoading(false);
};
const onForgotPasswordPressed = () => {
navigation.navigate('ForgotPassword');
}
const onRegisterPressed = () => {
navigation.navigate('Register')
}
return (
<KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : "height"} style={styles.container}>
<ScrollView contentContainerStyle={{flexGrow:1, justifyContent:'center'}} showsVerticalScrollIndicator={false}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.root}>
<Image source={Logo} style={[styles.logo, {height : height * 0.2}]} resizeMode={'contain'} />
<CustomInput icon='user' name='username' placeholder='Username' control={control} rules={{required: 'Username is required'}} />
<CustomInput icon='lock' name='password' placeholder='Password' control={control} rules={{required: 'Password is required'}} secureTextEntry={true} />
<CustomButton text={loading ? 'Loading...' : 'Login Account'} onPress={handleSubmit(onLoginPressed)} />
<CustomButton text='Forgot Password?' onPress={onForgotPasswordPressed} type='TERTIARY' />
<CustomButton text="Don't have an account? Create one" onPress={onRegisterPressed} type='TERTIARY' />
</View>
</TouchableWithoutFeedback>
</ScrollView>
</KeyboardAvoidingView>
);
};
const styles = StyleSheet.create({
root: {
alignItems: 'center',
padding: 20,
},
logo: {
width: 200,
maxWidth: 300,
maxHeight: 300,
},
});
export default LoginScreen;
Issue
At the time you are calling navigation.navigate('Home') there is no screen called in Home in Navigation because of that ternary that checks whether there is a user or not and renders conditionally your screens. That's why React Naviagation is not happy.
Solution
React Navigation's documention tell us that we shouldn't "manually navigate when conditionally rendering screens". Means you should find a way to call setUser(user) in Login where setUser is the state setter from Navigation component.
To do so we can use a context and for that change Navigation as follow (I added comments where I changed things):
import React, { createContext, useEffect, useState } from "react";
import { ActivityIndicator, View } from "react-native";
import ConfirmEmailScreen from "../screens/ConfirmEmailScreen";
import ForgotPassword from "../screens/ForgotPassword";
import HomeScreen from "../screens/HomeScreen";
import LoginScreen from "../screens/LoginScreen";
import NewPasswordScreen from "../screens/NewPasswordScreen";
import RegisterScreen from "../screens/RegisterScreen";
import { NavigationContainer } from "#react-navigation/native";
import { createNativeStackNavigator } from "#react-navigation/native-stack";
import { Auth, Hub } from "aws-amplify";
const Stack = createNativeStackNavigator();
// Line I added
export const AuthContext = createContext(null);
const Navigation = () => {
const [user, setUser] = useState(undefined);
//Checking if user is already logged in or not!
const checkUser = async () => {
try {
const authUser = await Auth.currentAuthenticatedUser({ bypassCache: true });
setUser(authUser);
} catch (e) {
setUser(null);
}
};
useEffect(() => {
checkUser();
}, []);
useEffect(() => {
const listener = (data) => {
if (data.payload.event === "signIn" || data.payload.event === "signOut") {
checkUser();
}
};
Hub.listen("auth", listener);
return () => Hub.remove("auth", listener);
}, []);
if (user === undefined) {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<ActivityIndicator />
</View>
);
}
return (
// Wrapper I added
<AuthContext.Provider value={{ user, setUser }}>
<NavigationContainer>
<Stack.Navigator>
{/*{If user is logged in navigate him to Homescreen else go throght the Screens based on the user selection */}
{user ? (
<Stack.Screen name="Home" component={HomeScreen} />
) : (
<>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
<Stack.Screen name="ConfirmEmail" component={ConfirmEmailScreen} />
<Stack.Screen name="ForgotPassword" component={ForgotPassword} />
<Stack.Screen name="NewPassword" component={NewPasswordScreen} />
</>
)}
</Stack.Navigator>
</NavigationContainer>
</AuthContext.Provider>
);
};
export default Navigation;
Consume AuthContext from Navigation in Login so you are able to call setUser after login, like below (I added comments where I changed thing):
import { useNavigation } from "#react-navigation/native";
import React, { useContext, useState } from "react";
import {
Alert,
Image,
Keyboard,
KeyboardAvoidingView,
ScrollView,
StyleSheet,
TouchableWithoutFeedback,
useWindowDimensions,
View,
} from "react-native";
import Logo from "../../../assets/images/logo-main.png";
import CustomButton from "../../components/CustomButton/CustomButton";
import CustomInput from "../../components/CustomInput/CustomInput";
import { Auth } from "aws-amplify";
import { useForm } from "react-hook-form";
// Line I added
import {AuthContext} from "./Navigation" // ⚠️ use the correct path
const LoginScreen = () => {
// Line I added
const { setUser } = useContext(AuthContext);
const [loading, setLoading] = useState(false);
const { height } = useWindowDimensions();
const {
control,
handleSubmit,
formState: { errors },
} = useForm();
const navigation = useNavigation();
const onLoginPressed = async (data) => {
if (loading) {
return;
}
setLoading(true);
try {
const user = await Auth.signIn(data.username, data.password);
// Line I added
setUser(user);
} catch (e) {
Alert.alert("Opps", e.message);
}
setLoading(false);
};
const onForgotPasswordPressed = () => {
navigation.navigate("ForgotPassword");
};
const onRegisterPressed = () => {
navigation.navigate("Register");
};
return (
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
style={styles.container}
>
<ScrollView
contentContainerStyle={{ flexGrow: 1, justifyContent: "center" }}
showsVerticalScrollIndicator={false}
>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.root}>
<Image
source={Logo}
style={[styles.logo, { height: height * 0.2 }]}
resizeMode={"contain"}
/>
<CustomInput
icon="user"
name="username"
placeholder="Username"
control={control}
rules={{ required: "Username is required" }}
/>
<CustomInput
icon="lock"
name="password"
placeholder="Password"
control={control}
rules={{ required: "Password is required" }}
secureTextEntry={true}
/>
<CustomButton
text={loading ? "Loading..." : "Login Account"}
onPress={handleSubmit(onLoginPressed)}
/>
<CustomButton
text="Forgot Password?"
onPress={onForgotPasswordPressed}
type="TERTIARY"
/>
<CustomButton
text="Don't have an account? Create one"
onPress={onRegisterPressed}
type="TERTIARY"
/>
</View>
</TouchableWithoutFeedback>
</ScrollView>
</KeyboardAvoidingView>
);
};
const styles = StyleSheet.create({
root: {
alignItems: "center",
padding: 20,
},
logo: {
width: 200,
maxWidth: 300,
maxHeight: 300,
},
});
I am trying to use the values/functions from my context but every time I console.log a value that Coes from context the value is undefined.
This is my Context:
import { NativeStackNavigationProp } from '#react-navigation/native-stack';
import React, {createContext, FC, useEffect } from 'react';
import { useState } from 'react';
import {fbInit, setNewUserFireStore, subscribeUserFS } from '../services/FirebaseServices';
const initialState = {
signedIn: false,
}
export const FirebaseContext = createContext();
export const FirebaseContextProvider = ({children}) => {
const [isUserSignedIn, setIsUserSignedIn] = useState(initialState.signedIn);
useEffect(() => {
fbInit();
})
const testLog = () => {
console.log("TAG Test Log Func")
}
return (
<FirebaseContext.Provider value={{isUserSignedIn, setIsUserSignedIn}}>
{children}
</FirebaseContext.Provider>
);
}
This is where I am trying to use the values (I am trying to log the values when I press the Button)
import { useNavigation } from '#react-navigation/native';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import {Button, Card, TextInput} from 'react-native-paper';
import { FirebaseContext } from '../../context/FirebaseContext';
import React, {useContext, useEffect, useState } from 'react';
const initialUserState = {
userName: 'Nicole Lopez',
password: '1001008888',
}
export default function LoginScreen() {
const [disabled, setDisabled] = useState(false);
const [userState, setUserState] = useState(initialUserState);
const { fbContext } = useContext(FirebaseContext);
const navigation = useNavigation();
const onInputChange = (field, value) => {
setUserState({
...userState,
[field]: value
})
}
useEffect(() => {
setDisabled(userState.userName.length === 0 || userState.password.length === 0);
}, [userState.userName, userState.password])
return (
<View style={styles.container}>
<Card style={styles.card}>
<TextInput
mode="outlined"
label="Förnamn & Efternman"
defaultValue={userState.userName}
onChangeText={text => onInputChange("username", text)}
right={<TextInput.Icon name="account" onPress={() => {
}}/>}
style={styles.textInput}/>
<TextInput
mode="outlined"
label="Anställningsnummer 100100xxxx"
defaultValue={userState.password}
right={<TextInput.Icon name="lock"/>}
onChangeText={text => onInputChange("password", text)}
style={styles.textInput}/>
</Card>
<View style={styles.buttonRow}>
<Button
color={disabled ? "gray" : undefined}
disabled={disabled}
mode={'contained'}
icon={'login'}
onPress={() => {
console.log("TAG isUserSignedIn: " + fbContext?.isuserSignedIn)
//fbContext?.testLog()
//console.log("TAG LOGIN BTN PRESSED")
//navigation.navigate('HomeScreen')
}}>Login</Button>
</View>
</View>
);
}
This is my App.js:
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import {StackScreens} from './src/helpers/types';
import { NavigationContainer, useNavigationContainerRef } from '#react-navigation/native';
import LoginScreen from './src/screens/LoginScreen/LoginScreen';
import HomeScreen from './src/screens/HomeScreen/HomeScreen';
import {doc, getFirestore, onSnapshot, setDoc, Unsubscribe as UnsubscribeFS} from 'firebase/firestore';
import { FirebaseContextProvider, FirebaseContext } from './src/context/FirebaseContext';
import { useContext } from 'react';
//import { navigationRef } from './RootNavigation';
//const userDocument = firestore().collection("users").doc('Cstl3bA9xwwH3UwHZJhA').get();
const Stack = createNativeStackNavigator();
export default function App() {
return (
<FirebaseContextProvider>
<Content />
</FirebaseContextProvider>
);
}
export const Content = () => {
const navigationRef = useNavigationContainerRef();
const firebaseContext = useContext({FirebaseContext});
return (
<NavigationContainer ref={navigationRef}>
<Stack.Navigator initialRouteName="LoginScreen">
<Stack.Screen name="LoginScreen" component={LoginScreen} />
<Stack.Screen name="HomeScreen" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
Don't know what I have missed, I have installed the packages, I have tried to log different values but all seem to be undefined :/
I believe it is undefined because you're trying to destructure the context value, maybe
const { fbContext } = useContext(FirebaseContext);
should be
const fbContext = useContext(FirebaseContext);
For someone getting undefined from context, check on which level you placed your context provider. If you want to use context in a component you should wrap this component in a context provider outside of this component!
Incorrect way
const Component = () => {
const { value1, value2 } = useContext(MyContext); //here context value will be undefined
return (
<ContextProvider>
//Component content
</ContextProvider>
)
}
Correct way
const ComponentA = () => {
const { value1, value2 } = useContext(MyContext);
return(//Component A content)
}
const ComponentB = () => {
return(
<ContextProvider>
<ComponentA/>
</ContextProvider>
)
}
I need help figuring out the right way to logout and return to the Welcome screen.
Currently, the RootStack queries the redux store for a token, and uses that to determine which group of screens will be available to the user.
If a token exists we go into a TabNavigator called OrgTabs. And that seems to be taking removing the parent navigator. And when I try to logout, the screen I want to navigate to doesn't exist within the navigation object.
Please help! I'd really appreciate
Rootstack.js
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
//screens
import Welcome from '../screens/Welcome';
import SignIn from '../screens/SignIn';
import UserSignIn from '../screens/UserSignIn.js';
import StaffSignIn from '../screens/StaffSignIn';
import StaffSignUp from '../screens/StaffSignUp';
import OrgSignIn from '../screens/OrgSignIn';
import OrgSignUp from '../screens/OrgSignUp';
import OrgHomeScreen from '../screens/org/OrgHomeScreen';
import OrgTabs from '../components/molecules/OrgTabs';
import TestScreen from '../screens/org/TestScreen';
// selecetors and hooks
import { useSelector } from 'react-redux';
import { selectToken, selectRefreshToken } from '../features/user/userSlice';
const Stack = createStackNavigator();
const RootStack = () => {
const token = useSelector(selectToken)
const refreshToken = useSelector(selectRefreshToken)
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: true,
headerTitle:'',
headerTransparent: true
}}
>
{ token ?
(<Stack.Group>
<Stack.Screen name="OrgTabs" component={OrgTabs} />
<Stack.Screen name="Welcome" component={Welcome} />
<Stack.Screen name="TestScreen" component={TestScreen} />
<Stack.Screen name="SignIn" component={SignIn} />
<Stack.Screen name="UserSignIn" component={UserSignIn} />
<Stack.Screen name="StaffSignIn" component={StaffSignIn} />
<Stack.Screen name="StaffSignUp" component={StaffSignUp} />
<Stack.Screen name="OrgSignIn" component={OrgSignIn} />
<Stack.Screen name="OrgSignUp" component={OrgSignUp} />
</Stack.Group>
)
:
// auth screens
(<Stack.Group>
<Stack.Screen name="Welcome" component={Welcome} />
<Stack.Screen name="SignIn" component={SignIn} />
<Stack.Screen name="UserSignIn" component={UserSignIn} />
<Stack.Screen name="StaffSignIn" component={StaffSignIn} />
<Stack.Screen name="StaffSignUp" component={StaffSignUp} />
<Stack.Screen name="OrgSignIn" component={OrgSignIn} />
<Stack.Screen name="OrgSignUp" component={OrgSignUp} />
<Stack.Screen name="OrgTabs" component={OrgTabs} />
</Stack.Group>
)
}
</Stack.Navigator>
</NavigationContainer>
);
}
export default RootStack;
StaffSignIn.js
import React from "react";
import { Text, TextInput, ActivityIndicator } from "react-native";
import { StatusBar } from 'expo-status-bar';
import { Formik, Form } from "formik";
import { useDispatch, useSelector } from "react-redux";
import {
LightContainer, PadlessContainer, FlexHoriztal,
Header1, Header2, Header3, TextLight,
AppLogo, StlyedButton,
Pad_h_medium, Pad_h_small, Pad_w_small} from "../styles/styles";
import { TextLink, MsgBox } from "../components/atoms/Atoms";
import { StyldTextInput } from "../components/molecules/Molecules";
import { StyledFormArea } from "../components/organisms/Organisms";
import KeyboardAvoidingWrapper from "../components/organisms/KeyboardAvoidingWrapper";
import * as SecureStore from "expo-secure-store";
import axios from "axios";
import { setToken } from "../features/user/userSlice";
import {getToken} from "../features/user/User";
const logo_img = require("../assets/logo_red.png");
const chalk = require('chalk');
const StaffSignIn = ({navigation}) => {
[message, setMessage] = React.useState("");
[messageStatus, setMessageStatus] = React.useState("failed");
[hidePassword, setHidePassword] = React.useState(true);
const dispatch = useDispatch();
const handleSubmit = (values, setSubmitting) => {
const url = "https://dandle.dustinc.dev/signin/staff";
axios.post(url, values)
.then( (response) => {
const result = response.data;
const {success, user, token, refreshToken} = response.data;
if(success === true) {
setMessageStatus("success");
setMessage("sign in successful");
storeToken(token, refreshToken);
dispatch(setToken(token));
navigation.navigate("OrgTabs");
}
else if (success === false) {
setMessageStatus("failed");
setMessage("sign in failed");
}
setSubmitting(false);
})
.catch(err => {
setSubmitting(false);
// setMessage("Oops! Network error. Try again soon");
if (err.response.status === 401) {
setMessage("Invalid username or password");
}
else {
setMessage("Oops! Network error. Try again soon");
}
})
.finally(() => {
setSubmitting(false);
});
}
// function that will store a token and refresh token in react-native-keychain
async function storeToken (token, refreshToken) {
SecureStore.setItemAsync("token", token)
.then(() => {
SecureStore.setItemAsync("refreshToken", refreshToken)
.then(() => {
dispatch(setToken(token));
dispatch(setToken(refreshToken));
console.log("\x1b[32m successfully stored token and refresh-token\n");
console.log("\x1b[0m token: ", token,'\n');
console.log("refreshToken: ", refreshToken, '\n');
})
.catch(err => {
console.log(err);
});
})
.catch(err => {
console.log(err);
});
}
return (
<KeyboardAvoidingWrapper>
<LightContainer>
<StatusBar style="dark" />
<PadlessContainer>
<Pad_h_medium /><Pad_h_medium />
<Formik
initialValues={{
username: "",
password: "",
}}
onSubmit={(values, {setSubmitting}) => {
handleSubmit(values, setSubmitting);
}}
>
{
({handleChange, handleBlur, handleSubmit, isSubmitting, values}) => (
<StyledFormArea justify='center'>
<StyldTextInput
label="Username"
placeholder="johndoe"
onChangeText={handleChange("username")}
value={values.username}
/>
<StyldTextInput
label="Password"
placeholder="* * * * * * *"
secureTextEntry={hidePassword}
value={values.password}
onChangeText={handleChange('password')}
isPassword={true}
hidePassword={hidePassword}
setHidePassword={setHidePassword}
/>
<Pad_h_medium /><Pad_h_medium /><Pad_h_medium />
<Pad_h_medium /><Pad_h_medium /><Pad_h_medium />
<MsgBox type={messageStatus}>{message}</MsgBox>
{!isSubmitting && <StlyedButton onPress={handleSubmit} width='100%'>
<TextLight>Sign in</TextLight>
</StlyedButton>}<Pad_h_small />
{isSubmitting && <StlyedButton width='100%' disabled={true}>
<ActivityIndicator size="small" color="#fff" />
</StlyedButton>}<Pad_h_small />
<FlexHoriztal justify='center'>
<Header2>Don't have an account?</Header2>
<TextLink onPress={()=> navigation.navigate('StaffSignUp')}>
<Header2>Register</Header2>
</TextLink>
</FlexHoriztal>
</StyledFormArea>
)
}
</Formik>
</PadlessContainer>
</LightContainer>
</KeyboardAvoidingWrapper>
);
}
export default StaffSignIn;
OrgTabs.js
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import {Octicons} from '#expo/vector-icons';
//screens
import OrgSettings from '../../screens/org/OrgSettings';
import OrgHomeScreen from '../../screens/org/OrgHomeScreen';
import OrgAnalytics from '../../screens/org/OrgAnalytics';
import OrgChat from '../../screens/org/OrgChat';
const Tab = createBottomTabNavigator();
export default function OrgTabs() {
return (
<NavigationContainer
independent={true}
>
<Tab.Navigator
screenOptions={{
tabBarShowLabel: false,
tabBarStyle: {
position: 'absolute',
backgroundColor: '#f38484',
borderTopRightRadius: 2,
borderTopLeftRadius: 2,
},
headerTransparent : true,
headerShown: false,
tabBarActiveBackgroundColor: '#d64547',
}}
>
<Tab.Screen
name="Settings"
component={OrgSettings}
options={{
tabBarIcon: () => {
return(
<Octicons name='gear' size={25} color='white'/>
)
}
}}
/>
<Tab.Screen
name="Home"
component={OrgHomeScreen}
options={{
tabBarIcon: () => {
return(
<Octicons name='home' size={25} color='white'/>
)
}
}}
/>
<Tab.Screen
name="Chat"
component={OrgChat}
options={{
tabBarIcon: ({size,focused,color}) => {
return(
<Octicons name='comment-discussion' size={25} color='white'/>
)
}
}}
/>
<Tab.Screen
name="Analytics"
component={OrgAnalytics}
options={{
tabBarIcon: ({size,focused,color}) => {
return(
<Octicons name='graph' size={25} color='white'/>
)
}
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
So the solution I came up with, was to display the message "Logged out" and then set the token in redux to Null.
And finally, reload the app using the Updates package, which is part of Expo.
So when you logout, the app is reloaded and the check for a token is repeated.
Try this,I Hope it will work.
const logout=()=>{
console.log("logout");
AsyncStorage.getAllKeys()
.then(keys => AsyncStorage.multiRemove(keys)).then(() => navigation.reset({ index: 0, routes: [{ name: "Login" }], }));
}
FoodList.js
import React, { Component } from "react";
import { View, Text, FlatList } from "react-native";
import { Content, List, ListItem } from "native-base";
import { useSelector, useDispatch } from "react-redux";
import { deleteFood } from "../../redux/actions/food";
const FoodList = () => {
const dispatch = useDispatch();
const deleteCurrent = (key) => dispatch(deleteFood(key));
const foods = useSelector((state) => state.foodReducer.FoodList); <------------
return (
<FlatList
data={foods}
keyExtractor={(item, index) => item.key.toString()}
renderItem={(data) => <ListItem title={data.item.name} />}
/>
);
};
export default FoodList;
FoodCreate.js
import { useDispatch } from "react-redux";
import { addFood } from "../../redux/actions/food";
const FoodCreate = ({ navigation: { goBack } }) => {
const [food, setFood] = useState("");
const dispatch = useDispatch();
const submitFood = (food) => dispatch(addFodd(food));
return (
<Container>
<Header>
<Left>
<Button transparent>
<Icon
name="arrow-back"
onPress={() => this.props.navigation.goBack()}
style={{ fontSize: 25, color: "red" }}
/>
</Button>
</Left>
<Body>
<Title>Add Food</Title>
</Body>
<Right>
<Button transparent>
<Icon
name="checkmark"
style={{ fontSize: 25, color: "red" }}
onPress={() => {
submitFood(food);
setFood("");
}}
/>
</Button>
</Right>
</Header>
<View style={{ alignItems: "center", top: hp("3%") }}>
<TextInput
placeholder="Food Name"
placeholderTextColor="white"
style={styles.inptFood}
value={food}
onChangeText={(food) => setFood(food)}
/>
</View>
actions/food.js
import { ADD_FOOD, DELETE_FOOD } from "./types";
export const addFood = (food) => ({
type: ADD_FOOD,
data: food,
});
export const deleteFood = (key) => ({
type: DELETE_FOOD,
key: key,
});
store/store.js
import { createStore, combineReducers } from "redux";
import foodReducer from "../reducers/foodReducer";
const rootReducer = combineReducers({
foodReducer: foodReducer,
});
const configureStore = () => createStore(rootReducer);
export default configureStore;
actions/types.js
export const ADD_FOOD = "ADD_FOOD";
export const DELETE_FOOD = "DELETE_FOOD";
reducers/foodReducer.js
import { ADD_FOOD, DELETE_FOOD } from "../actions/types";
const initialState = {
FoodList: [],
};
const foodReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_FOOD:
return {
...state,
FoodList: state.FoodList.concat({
key: Math.random(),
name: action.data,
}),
};
case DELETE_FOOD:
return {
...state,
FoodList: state.FoodList.filter((item) => item.key != action.key),
};
default:
return state;
}
};
export default foodReducer;
App.js
import FoodListScreen from "./components/Main/FoodList.js";
import configureStore from "./redux/store/store";
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const store =
(createStore(rootReducer, applyMiddleware(thunk)), configureStore());
return (
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="FoodCreate"
component={FoodCreateScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name="FoodList"
component={FoodListScreen}
options={{ headerShown: false }}
/>
</Stack.Navigator>
</NavigationContainer>
</Provider>
Hey everyone, I'm new to redux and I'm trying to use it my Diet App, the user creates a Food item in FoodCreate.js and it will be listed in FoodList.js, at the moment when I run the code it gives me back the following error: state.foodReducer.FoodList is undefined, I put an arrow next to the line of code that gives me the error, I used this approach by following the following YouTube tutorial: https://www.youtube.com/watch?v=jTJ6zo5GO7E, thank in advance for your help.
ok maybe i found
//In your store/store.js
import { createStore, combineReducers,applyMiddleware } from "redux";
import thunk from 'redux-thunk'
import foodReducer from "../reducers/foodReducer";
const configureStore = () => {
const store = createStore(
combineReducers({
foodReducer: foodReducer,
}),
applyMiddleware(thunk)
);
return store;
}
export default configureStore;
//In your App.js
...
import configureStore from "./redux/store/store";
const store = ConfigureStore();
return(
<Provider store={store}>
...
</Provider>
)
I am trying to call a function within useEffect, how would I mount this function within useEffect, I am not sure?
Initially useEffect is included as the following-
useEffect(() => {
CognitensorEndpoints.getDashboardList({
dispatchReducer: dispatchDashboards,
});
CognitensorEndpoints.getList({
dispatchReducer: dispatchDashboards,
});
},[]);
Then when I include function within useEffect, it returns error - 'Cannot read property 'filter' of undefined' -
useEffect(() => {
console.log('abcd');
setLoading();
CognitensorEndpoints.getDashboardList({
dispatchReducer: dispatchDashboards,
});
CognitensorEndpoints.getList({
dispatchReducer: dispatchDashboards,
});
},[]);
How do I correctly include the 'setLoading()' function within useEffect?
Here's the full app code-
import React, { useState, useEffect, useReducer } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, Keyboard, Button } from 'react-native';
import { Searchbar } from 'react-native-paper';
import { theme } from '../theme';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { apiStateReducer } from '../reducers/ApiStateReducer';
import CognitensorEndpoints from '../services/network/CognitensorEndpoints';
import DefaultView from '../components/default/DefaultView';
import DashboardListCard from '../components/DashboardListCard';
import DashboardHeader from '../components/DashboardHeader';
import DashboardGridCard from '../components/DashboardGridCard';
import {
NavigationContainer,
useFocusEffect,
} from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const AppHeader = ({
scene,
previous,
navigation,
searchIconVisible = false,
item,
index,
onPress
}) => {
const [dashboards, dispatchDashboards] = useReducer(apiStateReducer, {
data: [],
isLoading: true,
isError: false,
});
const [gridView, setGridView] = useState(false);
const toggleGridView = () => {
setGridView(!gridView);
};
const [filtered, setFiltered] = useState([]);
const setLoading = () => {
const messages = dashboards.data.message.filter((item) => {
const title = item.dashboardTitle || item.dashboardName;
return title.toLowerCase();
});
setFiltered(messages);
console.log(filtered);
};
const dropShadowStyle = styles.dropShadow;
const toggleSearchVisibility = () => {
navigation.navigate('Search');
};
useEffect(() => {
console.log(abcd);
setLoading();
CognitensorEndpoints.getDashboardList({
dispatchReducer: dispatchDashboards,
});
CognitensorEndpoints.getList({
dispatchReducer: dispatchDashboards,
});
},[]);
return (
<>
<View style={styles.header}>
<View style={styles.headerLeftIcon}>
<TouchableOpacity onPress={navigation.pop}>
{previous ? (
<MaterialIcons
name="chevron-left"
size={24}
style={styles.visible}
/>
) : (
<MaterialIcons
name="chevron-left"
size={24}
style={styles.invisible}
/>
)}
</TouchableOpacity>
</View>
{filtered.map(item => (
<Text style={styles.headerText}>
{item.dashboardTitle}
</Text>
))}
<View style={styles.headerRightIconContainer}>
{searchIconVisible ? (
<TouchableOpacity
style={[styles.headerRightIcon, dropShadowStyle]}
onPress={toggleSearchVisibility}>
<MaterialIcons name="search" size={24} style={styles.visible} />
</TouchableOpacity>
) : (
<View style={styles.invisible} />
)}
</View>
</View>
</>
);
};
Use setfilters in useEffect. it will be called when dashboards value changed.
useEffect(() => {
console.log(dashboards);
if(dashboards.data.message) {
const messages = dashboards.data.message.filter((item) => {
const title = item.dashboardTitle || item.dashboardName;
return title.toLowerCase();
});
setFiltered(messages);
console.log(filtered);
}
},[dashboards]);
import React, { useState, useEffect, useReducer } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, Keyboard, Button } from 'react-native';
import { Searchbar } from 'react-native-paper';
import { theme } from '../theme';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { apiStateReducer } from '../reducers/ApiStateReducer';
import CognitensorEndpoints from '../services/network/CognitensorEndpoints';
import DefaultView from '../components/default/DefaultView';
import DashboardListCard from '../components/DashboardListCard';
import DashboardHeader from '../components/DashboardHeader';
import DashboardGridCard from '../components/DashboardGridCard';
import {
NavigationContainer,
useFocusEffect,
} from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const AppHeader = ({
scene,
previous,
navigation,
searchIconVisible = false,
item,
index,
onPress
}) => {
const [dashboards, dispatchDashboards] = useReducer(apiStateReducer, {
data: [],
isLoading: true,
isError: false,
});
const [gridView, setGridView] = useState(false);
const toggleGridView = () => {
setGridView(!gridView);
};
const [filtered, setFiltered] = useState([]);
const setLoading = () => {
const messages = dashboards.data.message.filter((item) => {
const title = item.dashboardTitle || item.dashboardName;
return title.toLowerCase();
});
setFiltered(messages);
console.log(filtered);
};
const dropShadowStyle = styles.dropShadow;
const toggleSearchVisibility = () => {
navigation.navigate('Search');
};
useEffect(() => {
CognitensorEndpoints.getDashboardList({
dispatchReducer: dispatchDashboards,
});
CognitensorEndpoints.getList({
dispatchReducer: dispatchDashboards,
});
},[]);
useEffect(()={ if(dashboards.data.length > 0) { setLoading(); } },[dashboards]);
return (
<>
<View style={styles.header}>
<View style={styles.headerLeftIcon}>
<TouchableOpacity onPress={navigation.pop}>
{previous ? (
<MaterialIcons
name="chevron-left"
size={24}
style={styles.visible}
/>
) : (
<MaterialIcons
name="chevron-left"
size={24}
style={styles.invisible}
/>
)}
</TouchableOpacity>
</View>
{filtered.map(item => (
<Text style={styles.headerText}>
{item.dashboardTitle}
</Text>
))}
<View style={styles.headerRightIconContainer}>
{searchIconVisible ? (
<TouchableOpacity
style={[styles.headerRightIcon, dropShadowStyle]}
onPress={toggleSearchVisibility}>
<MaterialIcons name="search" size={24} style={styles.visible} />
</TouchableOpacity>
) : (
<View style={styles.invisible} />
)}
</View>
</View>
</>
);
};