how to use react navigation inside alert onpress? - javascript

i'm using react navigation v5,
i'm trying to navigate to login screen from alert when user press Ok,
but currently not working with error >>
can't find variable: navigation
The function is below :
async function handleSubmit(navigation) {
try {
const value = await AsyncStorage.getItem('storage_useremail');
if (value !== null) {
fetch('https://xxxxxx/logout', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: value,
}),
}).then(res => res.json())
.then(res => {
// console.log(res.err_code)
if (res.err_code == '0000') {
Alert.alert('Notice',res.message + " (" + res.err_code + ")",[{ text: 'Yes', onPress: () => navigation.navigate('Login')} ]);
} else {
Alert.alert("Response: " + res.err_code + " - " + res.message);
}
})
}
} catch (error) {
// Error retrieving data
}
}
function HomeNavigator() {
return (
<AppStack.Navigator mode="modal" initialRouteName="Login" screenOptions={({ route, navigation }) => ({
headerStyle: {
backgroundColor: '#fb5b5a',
borderBottomWidth: 0,
},
headerRight: () => (
<Button
onPress={() => handleSubmit()}
// onPress={() => navigation.navigate('Login')}
title="Logout"
color="#fff"
/>
),
})}>
<AppStack.Screen
name="Home"
component={HomeScreen}
options={{
title: 'Home',
}}
/>
<AppStack.Screen
name="Memory"
component={MemoryScreen}
options={{
title: 'Memory',
}} />
</AppStack.Navigator>
);
}
navigation.navigate('Login') << this the part where can't navigate.
Can somebody help this?
Thanks before

Because you don't pass navigation to handleSubmit
function HomeNavigator() {
return (
<AppStack.Navigator mode="modal" initialRouteName="Login" screenOptions={({ route, navigation }) => ({
headerStyle: {
backgroundColor: '#fb5b5a',
borderBottomWidth: 0,
},
headerRight: () => (
<Button
onPress={() => handleSubmit(navigation)} // <<<<<<<<<<<< Here
// onPress={() => navigation.navigate('Login')}
title="Logout"
color="#fff"
/>
),
})}>
<AppStack.Screen
name="Home"
component={HomeScreen}
options={{
title: 'Home',
}}
/>
<AppStack.Screen
name="Memory"
component={MemoryScreen}
options={{
title: 'Memory',
}} />
</AppStack.Navigator>
);
}

onPress should be a function declaration that will be triggered when pressed
onPress: () => navigation.navigate('Login')

Related

Reactjs: How to use a callback passed to this child component to update a parent component's state

The child component will add data and the parent component will display the latest data that the child component inserted, my problem is the useState didnt work properly, because the data is successfully saved but i cant see the data inserted in the parent component, need to refresh or load the page to see the latest data.
Parent component
const fetchData = async () => {
const response = await getDepartment('fmDepartments', 'fmDepartmentsId', 'All', 100, 0);
console.log(response)
response.data.map(function(u){
newUsers.push({
sequence:u.sequence,
name: u.name,
datecreated: u.datecreated,
dateupdated: u.dateupdated,
status: u.status,
actions: ([
<div className={classes.root}>
{u.status !== 1 ?(
<Tooltip title={<span style={{fontSize: 15, fontWeight: 600,}}>Deactivate</span>} arrow>
<IconButton
aria-label="Deactivate"
color="primary"
size="small"
className={classes.deactivateButton}
onClick={() => {handleActivate(u.id)}}
>
<HighlightOffIcon />
</IconButton>
</Tooltip>
):(
<Tooltip title={<span style={{fontSize: 15, fontWeight: 600,}}>Activate</span>} arrow>
<IconButton
aria-label="Active"
color="primary"
size="small"
className={classes.activateButton}
onClick={() => {handleDeactivate(u.id)}}
>
<CheckCircleOutlineIcon />
</IconButton>
</Tooltip>
)
}
</div>
])
})
})
setNewUserLists(newUsers)
}
useEffect(() => {
fetchData();
}, [reRender]);
const options = {
filter: true,
filterType: 'checkbox',
selectableRows: 'none',
print: false,
customToolbar: () => {
return (
<CustomToolbar/> //here is the link to the child component
)
},
setFilterChipProps: (colIndex, colName, data) => {
return {
color: 'primary',
variant: 'outlined',
className: 'testClass123',
};
}
};
return (
<>
<MUIDataTable
title={"Department"}
data={newUserLists}
columns={columns}
options={options}
/>
</>
)
child component
class CustomToolbar extends React.Component {
constructor(props) {
super(props)
this.HandleAdd = this.HandleAdd.bind(this);
this.state =false //here
}
HandleAdd = () => {
Swal.fire({
title: 'Add Department',
text: "Input department name below.",
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Save',
html: generateInputForms({
strname: '',
intsequence: ''
}),
preConfirm: () => {
let strname = document.getElementById('strname').value;
let intsequence = document.getElementById('intsequence').value;
if (!strname) {
Swal.showValidationMessage('The Department field is required.')
}
if (!intsequence) {
Swal.showValidationMessage('The Sequence field is required.')
}
return {
strname: document.getElementById('strname').value,
intsequence: document.getElementById('intsequence').value
}
}
}).then((result) => {
if (result.isConfirmed) {
let request = {
strresourcename: "Richard",
strapplicationcode: "SchoolApp",
strmodulename: "Department",
strtablename: "fmDepartments",
strfieldid: "fmDepartmentsId",
strname:document.getElementById('strname').value,
intsequence:document.getElementById('intsequence').value
}
addDepartment(request).then(res =>{
if (res.status == 200){
Swal.fire({
icon: 'success',
title: 'Department',
text: 'New Department has been added successfully.',
}).then(res => {
this.setState(!this.state); //here
})
}else{
Swal.fire({
icon: 'error',
title: 'Oops',
text: 'Something went wrong.',
})
}
})
}
})
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<Tooltip title={"Add"}>
<Button
variant="contained"
color="primary"
size="small"
style={{
textTransform: 'unset',
outline: 'none',
marginLeft: 20,
backgroundColor: '#00B029',
}}
onClick={this.HandleAdd}
className={classes.button}
startIcon={<AddIcon className={classes.addIcon} style={{color: '#fff',}} />}
>
Add
</Button>
</Tooltip>
</React.Fragment>
);
}
}

How to successfully pass params between screens?

I have been reading a lot of answers on SO as well as GitHub issues trying to implement a solution, but have been unable to come up with a solution for this situation.
Here is a representation of my code:
class MeetingsScreen extends React.Component {
constructor(props) {
super(props)
this.state = {
upcomingMeeting: [],
refreshing: false
};
}
async handlePress() {
const user = await AsyncStorage.getItem('User');
this.props.navigation.navigate('WriteSummary', { id: user.id, type: 'submit' });
}
printUpcomingMeetings = () => {
return (<View style={styles.meeting}>
<Button
containerStyle={styles.meetingButton}
style={styles.meetingButtonText}
onPress={() => this.handlePress(user.id)}
Press me to write summary!
</Button>
</View>);
}
}
render () {
return (<View style={{flex:1}} key={this.state.refreshing}>
{ this.printUpcomingMeetings() }
</View>);
}
}
class WriteSummaryScreen extends React.Component {
constructor(props) {
super(props)
this.state = {
storageId: '',
normalId: -1,
type: '',
curSummary: ''
}
}
componentDidMount = () => {
const { params } = this.props.navigation.state;
const { id, type } = params ? params : null;
const storageId = 'summary_' + id;
this.setState({storageId:storageId});
this.setState({normalId:id});
this.setState({type:type});
AsyncStorage.getItem(storageId).then((value) => this.setSkipValue(value));
}
async setSkipValue (value) {
if (value !== null) {
this.setState({ 'curSummary': value });
} else {
this.setState({ 'curSummary': '' });
}
}
async saveSummary (text) {
this.setState({'curSummary': text});
await AsyncStorage.setItem(this.state.storageId, text);
}
async handleSubmit() {
const user = await AsyncStorage.getItem('User');
if (this.state.type === 'submit') {
// post insert
const postres = fetch (url + '/create-summary', {
method: 'POST',
body: JSON.stringify({
AppointmentId: this.state.normalId,
SummaryText: this.state.curSummary,
UserId: user.Id
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
})
.catch((error) => {
console.error(error);
});
} else {
// post update
const postres = fetch (url + '/update-summary', {
method: 'POST',
body: JSON.stringify({
AppointmentId: this.state.normalId,
SummaryText: this.state.curSummary,
UserId: user.Id
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
})
.catch((error) => {
console.error(error);
});
}
this.props.navigation.navigate('Meetings');
}
render () {
return <View style={{flex:1}}>
<View>
<View style={{height:25, backgroundColor: colors.vikingBlue}}></View>
<View style={{height:30, backgroundColor: colors.white}}></View>
<View style={{flexDirection:'row', backgroundColor: colors.white, alignItems:'center'}}>
<View style={{width:5}}></View>
<TouchableOpacity onPress={() => this.props.navigation.goBack()} activeOpacity={0.5}>
<Image style={{width:30, height:30}} source={require('./assets/icons8-back-50.png')} />
</TouchableOpacity>
<View style={{width:10}}></View>
<View style={{width:mainTitleWidth,textAlign:'center',alignItems:'center'}}>
<Text style={{fontSize:22}}>Settings</Text>
</View>
<TouchableOpacity onPress={() => this.props.navigation.navigate('HelpModal')} activeOpacity={0.5}>
<Image style={{width:30, height:30}} source={require('./assets/help.png')} />
</TouchableOpacity>
</View>
<View style={{height:30, backgroundColor: colors.white}}></View>
</View>
<TextInput
multiline
numberOfLines={6}
style={styles.summaryInput}
onChangeText={text => saveSummary(text)}
value={this.state.curSummary} />
<Button
containerStyle={styles.summaryButton}
style={styles.summaryButtonText}
onPress={this.handleSubmit()}>
Submit
</Button>
</View>
}
}
function HomeStack() {
return (
<Tab.Navigator
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Meetings" component={MeetingsScreen} />
<Tab.Screen name="Topics" component={TopicsScreen} />
</Tab.Navigator>
);
}
export default class AppContainer extends React.Component {
// Main rendering function. Always begins on the SplashScreen, which checks user login status and directs to Meetings. I left it out and the other Tab navigator screens for less clutter.
render() {
return (
<NavigationContainer>
<Stack.Navigator headerMode='none' initialRouteName='Splash'>
<Stack.Screen name='Splash' component={SplashScreen} />
<Stack.Screen name='Main' component={HomeStack} />
<Stack.Screen name='WriteSummary' component={WriteSummaryScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
};
I get the error TypeError: undefined is not an object (evaluating '_this7.props.navigation.state.params)' after pressing the button on MeetingsScreen and navigating to WriteSummaryScreen.
What confuses me is the screen is navigated to, so the data should have been passed. What am I missing?
You can access parametes like below if its class based component:
class WriteSummaryScreen extends React.Component {
const {id, type} = this.props.route.params;
//........
for the functional component:
const WriteSummaryScreen =({navigation, route})=>{
const {id, type} = route.params;
//..........
}
You can access the params that you have passed from a screen using the getParam function.
so you can use this code:
const id = this.props.navigation.getParam("id");
const type = this.props.navigation.getParam("type");
What ended up working for me was the following:
const id = this.props.route.params.id;
const type = this.props.route.params.type;

React Navigation 5, CustomDrawer, Can't perform a React state update on context changed

on App.js, im using useContext to check of user logged in to switch active navigation,
Then, on Login screen after successful login, I got error "Can't perform a React state update on LoginScreen" after the context has changed.
I'm pretty sure it comes from Drawer, because when i try to logout and login back, the drawer opened immediately. but i didn't use any useState to handle login or logout, all my async function only used when handling login or logout not in useState.
you can find my sample video here:
https://vimeo.com/user125707637/review/471649007/2c9dd00d1a
you can see on the video, the drawer opened automatically after the error comes out. each time you login back, the drawer was open and need to close manually. Any idea? or it's a bug.. idk
App.js:
const App = ({navigation, route}) => {
const [user, setUser] = useState();
const [loadingVisible, setLoadingVisible] = useState(true);
const restoreAuth = async () => {
try {
const response = await userApi.getUser();
setUser(response.data);
onFinishLoad();
} catch (error) {
if (error.response.status === 401) {
return onFinishLoad();
}
await storage.removeToken();
setUser(null);
onFinishLoad();
}
};
const onFinishLoad = async () => {
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
await delay(500);
setLoadingVisible(false);
};
useEffect(() => {
let mounted = true;
if (mounted) {
restoreAuth();
}
return () => (mounted = false);
}, []);
return (
<StyleProvider style={getTheme(material)}>
<AuthContext.Provider value={{user, setUser}}>
<LoadingComponent visible={loadingVisible} />
<Container>
<NavigationContainer>
{user ? <AppDrawerNavigation /> : <AuthNavigation />} // checking context changes
</NavigationContainer>
</Container>
</AuthContext.Provider>
</StyleProvider>
);
};
export default App;
Auth Navigator:
const AuthNavigation = () => {
return (
<Stack.Navigator>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{
headerShown: false,
}}
/>
<Stack.Screen name="Register" component={RegisterScreen} />
</Stack.Navigator>
);
};
LoginScreen:
const LoginScreen = ({navigation}) => {
const {user, setUser} = useContext(AuthContext);
const [loadingVisible, setLoadingVisible] = useState(false);
const validationSchema = Yup.object().shape({
email: Yup.string().required().label('E-mail'),
password: Yup.string().required().min(6).label('Password'),
});
const handleSubmit = async ({email, password}, actions) => {
setLoadingVisible(true);
try {
const response = await authApi.login(email, password, 'mobile');
storage.storeToken(response.data.token);
setUser(response.data.user);
setLoadingVisible(false);
} catch (error) {
const statusCode = error.response.status;
const errors = error.response.data;
const fieldErrors = errors?.errors;
const errorMessage = errors?.message;
setLoadingVisible(false);
if (statusCode === 422) {
for (let field in fieldErrors) {
actions.setFieldError(field, fieldErrors[field][0]);
}
} else {
Alert.alert(
'Login Failed',
`${errorMessage}`,
[{text: 'Ok', onPress: () => console.log('ok')}],
{cancelable: false},
);
}
}
};
return (
<Container>
<LoadingComponent visible={loadingVisible} />
<Content padder>
<View>
<Form
initialValues={{
email: '',
password: '',
}}
onSubmit={handleSubmit}
validationSchema={validationSchema}>
<FormField
name="email"
placeholder="E-mail"
keyboardType="email-address"
autoCorrect={false}
/>
<FormField
name="password"
autoCapitalize="none"
autoCorrect={false}
placeholder="Password"
secureTextEntry={true}
/>
<SubmitButton title="LOGIN" />
</Form>
<View style={{marginTop: 20}}>
<Text
style={{
flex: 1,
textAlign: 'center',
textDecorationLine: 'underline',
marginBottom: 10,
}}
onPress={() => navigation.navigate('Register')}>
Create an account
</Text>
<Text
style={{
flex: 1,
textAlign: 'center',
textDecorationLine: 'underline',
}}
onPress={() => navigation.navigate('ForgotPassword')}>
Forgot Password?
</Text>
</View>
</View>
</Content>
</Container>
);
};
Drawer Navigator:
const AppDrawerNavigation = () => {
return (
<Drawer.Navigator
initialRouteName="HomeStack"
drawerContentOptions={{
activeBackgroundColor: colors.primary,
activeTintColor: colors.light,
}}
drawerContent={(props) => <DrawerContent {...props} />}> // Render my custom Drawer
<Drawer.Screen
name="HomeStack"
component={HomeStackNavigation}
options={{
drawerLabel: ({color, focused}) => (
<Text style={{fontSize: !focused ? 14 : 16, color: color}}>
Entries
</Text>
),
drawerIcon: ({color, size, focused}) => (
<AntDesign
name="home"
color={color}
size={!focused ? size : size + 2}
/>
),
}}
/>
</Drawer.Navigator>
);
DrawerContent:
export const DrawerContent = (props) => {
const {user, setUser} = useContext(AuthContext);
const [loadingVisible, setLoadingVisible] = useState(false);
const handleLogout = () => {
Alert.alert(
null,
'Are you sure to logout?',
[
{
text: 'Cancel',
onPress: () => console.log('Cancel Pressed'),
style: 'cancel',
},
{
text: 'OK',
onPress: _logout,
},
],
{cancelable: false},
);
};
//handling logout
const _logout = async () => {
props.navigation.closeDrawer();
setLoadingVisible(true);
try {
const response = await authApi.logout();
await storage.removeToken();
setUser(null); // set user context to null
setLoadingVisible(false);
} catch (error) {
setLoadingVisible(false);
Alert.alert(
'Error',
'Failed to logout',
[
{
text: 'OK',
onPress: console.log('ok'),
},
],
{cancelable: false},
);
}
};
return (
<View style={{flex: 1}}>
<LoadingComponent visible={loadingVisible} />
<DrawerContentScrollView {...props}>
<View style={styles.drawerContent}>
<View style={styles.userInfoSection}>
<View style={{paddingVertical: 20}}>
<Text style={{fontSize: 28}}>SMART</Text>
<Text style={{fontSize: 16}}>Incident Management System</Text>
</View>
</View>
<Drawer.Section style={styles.drawerSection}>
<DrawerItemList {...props} />
</Drawer.Section>
</View>
</DrawerContentScrollView>
<Drawer.Section style={styles.bottomDrawerSection}>
<DrawerItem
icon={({color, size}) => (
<AntDesign name="logout" color={color} size={size} />
)}
label="Sign Out"
onPress={handleLogout}
/>
</Drawer.Section>
</View>
);
};

Passing params from StackNavigator to TabNavigator

I am a new React-native programmer . I am trying to pass params from a StackNavigator to TabNavigator but when I tried to print the params by using this.props.navigation.state.params , it is undefined.
I am trying to pass the user's name from fetch to a new screen
Auth.js
export const onVerify = (email,password,navigation) => {
console.log('Verifying');
fetch('xxx',
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'email=' + email + '&password=' + password
})
.then((response) => response.json())
.then((responseJson) => {
if(responseJson.status == '200') {
Alert.alert(
'Login Successful',
'Welcome Home'
);
let data = {
name: responseJson.name,
id : responseJson.id
};
console.log(data.name)
onSignIn().then(() => navigation.navigate("SignedIn"),data);
}
})
.catch((error) => {
console.error(error);
});
}
And here is my router.js
export const SignedOut = StackNavigator({
SignUp: {
screen: SignUp,
navigationOptions: {
title: "Sign Up",
headerStyle
}
},
SignIn: {
screen: SignIn,
navigationOptions: {
title: "Sign In",
headerStyle
}
}
});
export const SignedIn = TabNavigator(
{
Home: {
screen: Home,
navigationOptions: {
tabBarLabel: "Home",
tabBarIcon: ({ tintColor }) => (
<FontAwesome name="home" size={30} color={tintColor} />
)
}
},
Profile: {
screen: Profile,
navigationOptions: {
tabBarLabel: "Profile",
tabBarIcon: ({ tintColor }) => (
<FontAwesome name="user" size={30} color={tintColor} />
)
}
}
},
{
tabBarOptions: {
style: {
paddingTop: Platform.OS === "android" ? StatusBar.currentHeight : 0
}
}
}
);
Any ideas ?
You are sending params the wrong way,
Old:
onSignIn().then(() => navigation.navigate("SignedIn"),data); //wrong
New:
onSignIn().then(() => navigation.navigate("SignedIn", data)); //correct
From the docs of React-Navigation,
Pass params to a route by putting them in an object as a second
parameter to the navigation.navigate function:
this.props.navigation.navigate('RouteName', { /* params go here */ })

Text Input onSubmit while using fetch React Native

I am trying to update my state for nickname and email through textInput and then submit it through a post request to my server. Both keep coming back as undefined. I'm totalling lost as to what I'm doing wrong here.
export default class Add extends Component {
constructor(props){
super(props);
this.state ={
isLoading: true,
nickname: "",
email: ""
}
}
static navigationOptions = {
title: 'Add Contacts',
};
componentDidMount(){
this.setState({
isLoading: false
})
}
onSubmit = () => {
console.log('on submit')
return fetch(`...`, {
method: 'POST',
headers: {
'Accept': 'application/json, text/plain */*',
'Content-Type': 'application/json',
},
body: JSON.stringify(this.state)
})
.then((response) => response)
.then((responseJson) => {
return responseJson
})
.catch((error) => {
throw error;
})
}
render() {
const { navigate } = this.props.navigation;
if(this.state.isLoading){
return(
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
return(
<View
style={styles.container}
>
<Text
style={{fontSize:30}}>
New Contact
</Text>
<View>
<TextInput
ref= {(el) => { this.nickname = el; }}
onChangeText={(nickname) => this.setState({nickname}, function () {console.log('Nickname updated')})}
value={this.state.nickname}
/>
<TextInput
value={this.state.email}
onChangeText={(text) => this.setState({email: text})}
/>
</View>
<Button
text = {
<Text
style={{color:colors.textColor, textAlign: 'center'}}
>
Save
</Text>
}
onPress= { () => {
this.onSubmit()
navigate('Home')
}
}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
marginTop: 20,
alignItems: 'center',
justifyContent: 'center',
}
});
.then((response) => response)
Should be:
.then((response) => response.json())
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Making_fetch_requests

Categories