React Navigation autoFocus when using TabNavigator - react-native

In react-navigation, what is the best way to handle a tab that has a form with an autoFocus input that automatically pulls up the keyboard?
When the Navigator initializes all the screens, it automatically displays the keyboard even though the screen without the autoFocus element is showing first.
I want it to open the keyboard when I'm on the tab with the form, but close it when I leave that view.
Here is an example (and an associated Gist):
App.js
const AppNavigator = TabNavigator( {
listView: { screen: TheListView },
formView: { screen: TheFormView }
} )
TheFormView.js
const TheFormView = () => {
return (
<View style={{ marginTop: 50 }}>
<TextInput
autoFocus={ true }
keyboardType="default"
placeholder="Blah"
/>
</View>
)
}
TheListView.js
const TheListView = () => {
return (
<View style={{ marginTop: 50 }}>
<Text>ListView</Text>
</View>
)
}

You should use lazy on TabNavigator config: https://github.com/react-community/react-navigation/blob/master/docs/api/navigators/TabNavigator.md#tabnavigatorconfig
This prevents the screen from being initialised before it's viewed.
Also consider having some kind of state management or look for Custom Navigators (https://reactnavigation.org/docs/navigators/custom) for setting the autoFocus prop as true only when TheFormView is navigated to.

This answer was out of date for me as of April 2020, but this worked for me:
import { useFocusEffect, } from "#react-navigation/native"
import React, { useEffect, useState, } from "react"
...
const CreateProfileScreen = ({ navigation, }) => {
const [safeToOpenKeyboard, setSafeToOpenKeyBoard] = useState(false)
...
useFocusEffect(
React.useCallback(() => {
console.log("Navigated to CreateProfileScreen")
setSafeToOpenKeyBoard(true)
return () => {
console.log("Navigated away from CreateProfileScreen")
setSafeToOpenKeyBoard(false)
}
}, [])
)
...
return (<TextInput autoFocus={safeToOpenKeyboard}/>)
}

Related

Reset state value after navigation.goBack() to parent component didn't work

In my React Native 0.70 app, there are 2 components Home (parent) and ListSearch (child). Users enter server string in Home and search result is displayed in ListSearch. When users click navigation.goBack() on ListSearch to go back to Home, useFocusEffect from react navigation 6.x is used to reset the placeholder on search bar in Home. Here is the code in Home (parent) to reset the placeholder:
export default Home = ({ navigation}) => {
const searchHolder = "Enter search string here";
const [plcholder, setPlcholder] = useState(searchHolder);
const submitsearch = async () => {
...
setPlcholder(searchHolder);//reset place holder
navigation.navigate("ListSearch", {artworks:res, title:"Search Result"}). //<<==ListSearch is component of result displaying
}
//reset placeholder whenever the Home is focused.
useFocusEffect(
React.useCallback(() => {
setPlcholder(searchHolder); // reset the place holder search bar
},[navigation])
);
//view
return (
...
<View style={{flex:5}}>
<TextInput style={{fontSize:hp("3%")}} placeholder={plcholder} onChangeText={strChg}></TextInput>. //plcholder here
</View>
)
}
The code above didn't work. When users navigation.goBack() to Home component, the placeholder in search bar was the previous search string and was not updated.
Placeholder string is updated when you navigate to ListSearch, You should set value of TextInput to empty string, here is the code you can refer,
import { useState,useEffect } from 'react';
import { View, TextInput, TouchableOpacity, Text } from 'react-native';
export default Home = ({ navigation }) => {
const searchHolder = 'Enter search string here';
const [plcholder, setPlcholder] = useState(searchHolder);
const [text, setText] = useState();
const submitsearch = () => {
console.log('submitsearch called ', searchHolder);
setText("");
setPlcholder(searchHolder);
navigation.navigate('ListSearch');
};
//view
return (
<View style={{ flex: 5 }}>
<TouchableOpacity onPress={() => submitsearch()}>
<Text>Submit</Text>
</TouchableOpacity>
<TextInput
style={{ fontSize: 20, marginTop: 30 }}
placeholder={plcholder}
value={text}
onChangeText={(val) => setText(val)} />
</View>
);
};
Here is the demo, I've created.

Clearing Formik errors and form data - React Native

I'm using Formik and was wondering how I go about clearing the errors and form values when leaving a screen.
For example, a user tries to submit the form with no values and the errors are displayed:
When the user then navigates to a different screen and then comes back those errors are still present. Is there a way to clear these? Can I access Formik methods within a useEffect hook as an example?
This is my implementation so far:
export const SignIn = ({route, navigation}) => {
const formValidationSchema = Yup.object().shape({
signInEmail: Yup.string()
.required('Email address is required')
.email('Please provide a valid email address')
.label('Email'),
signInPassword: Yup.string()
.required('Password is required')
.label('Password'),
});
const initialFormValues = {
signInEmail: '',
signInPassword: '',
};
return (
<Formik
initialValues={initialFormValues}
validationSchema={formValidationSchema}
onSubmit={(values, formikActions) => {
handleFormSubmit(values);
}}>
{({handleChange, handleSubmit, errors}) => (
<>
<SignInForm
messages={errors}
navigation={navigation}
handleFormSubmit={handleSubmit}
/>
</>
)}
</Formik>
)
}
The problem here is that a screen does not get unmounted if we navigate to a different screen and the initial values of a Formik form will only be set on screen mount.
I have created a minimal example with one field and I navigate whenever the submit button is executed to a different Screen in a Stack.Navigator.
Notice that the onSubmit function is usually not fired if there are errors in your form fields. However, since I wanted to provide a quick test, I navigate by hand by calling the function directly.
If we navigate back by pressing the onBack button of the navigator, the form fields will be reseted to the default values and all errors will be reseted automatically.
We can trigger this by hand using the Formik innerRef prop and a focus listener.
For testing this, you should do the following.
Type something, and remove it. Notice the error message that is rendered below the input field.
Navigate to the screen using the submit button.
Go back.
Expected result: no error message.
Type something. Expected result: no error message.
Navigate on submit.
Go back.
Expected result: no error message, no content in field.
In principal, this will work with every navigator, e.g. changing a Tab in a Tab.Navigator and it will reset both, the errors and the field's content.
The key part is given by the following code snippet.
const ref = useRef(null)
const initialFormValues = {
signInEmail: '',
signInPassword: '',
};
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
if (ref?.current) {
ref.current.values = initialFormValues
ref.current.setErrors({})
console.log(ref.current)
}
});
return unsubscribe;
}, [navigation, initialFormValues]);
return (
<Formik
innerRef={ref}
isInitialValid={true}
initialValues={initialFormValues}
validationSchema={formValidationSchema}
onSubmit={(values) => {
console.log("whatever")
}}>
...
The full code is given as follows.
import React, { useRef} from 'react';
import { StyleSheet, Text, View,TextInput, Button } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { Formik } from 'formik';
import * as Yup from 'yup';
export const SignIn = ({route, navigation}) => {
const formValidationSchema = Yup.object().shape({
signInEmail: Yup.string()
.required('Email address is required')
.label('Email'),
});
const ref = useRef(null)
const initialFormValues = {
signInEmail: '',
signInPassword: '',
};
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
if (ref?.current) {
ref.current.values = initialFormValues
ref.current.setErrors({})
console.log(ref.current)
}
});
return unsubscribe;
}, [navigation, initialFormValues]);
function handleFormSubmit(values) {
navigation.navigate("SomeScreen")
}
return (
<Formik
innerRef={ref}
isInitialValid={true}
initialValues={initialFormValues}
validationSchema={formValidationSchema}
onSubmit={(values) => {
console.log("whatever")
}}>
{({handleChange, handleSubmit, errors, values}) => (
<>
<View>
<TextInput
style={{height: 30}}
placeholder={"Placeholder mail"}
onChangeText={handleChange('signInEmail')}
value={values.signInEmail}
/>
{errors.signInEmail ?
<Text style={{ fontSize: 10, color: 'red' }}>{errors.signInEmail}</Text> : null
}
<Button onPress={() => {
handleSubmit()
handleFormSubmit()}} title="Submit" />
</View>
</>
)}
</Formik>
)
}
export function SomeOtherScreen(props) {
return <></>
}
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Form" component={SignIn} />
<Stack.Screen name="SomeScreen" component={SomeOtherScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
This should be possible with the useFocusEffect hook from react navigation. It gets triggered on first time and every time the screen comes in focus.
useFocusEffect(
React.useCallback(() => {
// set Formik Errors to empty object
setErrors({});
// Since your clearing errors, you should also reset the formik values
setSignInEmail('');
setSignInPassword('');
return () => {}; // gets called on unfocus. I don't think you need this
}, [])
);

Call onPress method from imported UI navbar component

I am building an app that displays jobs and the commute time for each of them. I have a JobsComponent, where I display the jobs. In this component, I have added an icon on the navbar. When the icon is tapped, a MapComponent should be opened. In this map, I want to display a pin for each job.
The problem that I'm facing is that I've defined the icon in my AppNavigator.js and I want to have the onPress() functionality in JobsComponent.js, but I don't know how to do this. What I've tried:
Adding an ({ onPress }) param to navHeaderRight:
export const navHeaderRight = ({ onPress }) =>
(/UI component goes here/
)
but with no results.
One other idea I had was to define the onPress() behaviour in AppNavigator.js, but this means importing a lot of stuff (array of jobs, details for each job) to AppNavigator.js, which is not a good design decision from my point of view.
I tried just doing a console.log in navHeaderRight.onPress from JobsComponent to see if it works at all. It doesn't.
This is my AppNavigator.js:
import {TouchableHighlight, Image, View} from 'react-native';
import MapComponent from './MapComponent';
export const navHeaderRight = ({ onPress }) =>
(
<View style={{marginRight: 5}}>
<TouchableHighlight underlayColor="transparent">
<Image
source={require('../assets/map.png')}
style={{height: 40, width: 40}}/>
</TouchableHighlight>
</View>
)
const Navigator = createStackNavigator({
Jobs: {
screen: Jobs,
navigationOptions: {
headerRight: navHeaderRight
}
},
MapComponent: {
screen: MapComponent,
navigationOptions: {
header: null
}
}
//other screens defined in the navigator go here
});
const AppNavigator = createAppContainer(Navigator);
export default AppNavigator;
And this is my JobsComponent.js. Here, I try to define the onPress() behaviour in componentDidMount().
import {navHeaderRight} from './AppNavigator';
class Jobs extends Component {
componentDidMount() {
navHeaderRight.onPress = () => {
this.props.navigation.navigate('MapComponent', {/*other params go here*/})
}
}
}
Expected result: when navHeaderRight.onPress is called, the MapComponent should be opened.
**
Actual result: Nothing happens.
**
Any help will be greatly appreciated. :)
You can use React Navigation route parameters to achieve this.
In AppNavigator.js:
import { TouchableHighlight, Image, View } from 'react-native';
import MapComponent from './MapComponent';
const navHeaderRight = (navigation) => {
const handlePress = navigation.getParam('handlePress', null);
return (
<View style={{marginRight: 5}}>
<TouchableHighlight
underlayColor="transparent"
onPress={handlePress}
>
<Image
source={require('../assets/map.png')}
style={{height: 40, width: 40}}
/>
</TouchableHighlight>
</View>
);
}
const Navigator = createStackNavigator({
Jobs: {
screen: Jobs,
navigationOptions: ({ navigation }) => ({
headerRight: navHeaderRight(navigation),
}),
},
MapComponent: {
screen: MapComponent,
navigationOptions: {
header: null,
}
}
});
const AppNavigator = createAppContainer(Navigator);
export default AppNavigator;
In JobsComponent.js:
class Jobs extends Component {
componentDidMount() {
const handlePress = () => console.log('whatever function you want');
this.props.navigation.setParams({ handlePress });
}
// [...]
}

Pass Data between Pages in React native

Im new to react native and I'm stuck at following.
Im performing navigation (when clicked on alert view button) using the code below.
const {navigation} = this.props.navigation;
…
.
.
{ text: 'Done', onPress:() => {
navigate.push(HomeScreen);}
How can I pass data to another Page in React native? Can I declare the parameter global and just assign to it?
What would be the correct way of performing this and how would I go about it?
Note
This answer was written for react-navigation: "3.3.0". As there are newer versions available, which could bring changes, you should make sure that you check with the actual documentation.
Passing data between pages in react-navigation is fairly straight forward. It is clearly explained in the documentation here
For completeness let's create a small app that allows us to navigate from one screen to another passing values between the screens. We will just be passing strings in this example but it would be possible to pass numbers, objects and arrays.
App.js
import React, {Component} from 'react';
import AppContainer from './MainNavigation';
export default class App extends React.Component {
render() {
return (
<AppContainer />
)
}
}
MainNavigation.js
import Screen1 from './Screen1';
import Screen2 from './Screen2';
import { createStackNavigator, createAppContainer } from 'react-navigation';
const screens = {
Screen1: {
screen: Screen1
},
Screen2: {
screen: Screen2
}
}
const config = {
headerMode: 'none',
initialRouteName: 'Screen1'
}
const MainNavigator = createStackNavigator(screens,config);
export default createAppContainer(MainNavigator);
Screen1.js and Screen2.js
import React, {Component} from 'react';
import { View, StyleSheet, Text, Button } from 'react-native';
export default class Screen extends React.Component {
render() {
return (
<View style={styles.container}>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
}
});
Here we have 4 files. The App.js which we will import the MainNavigation.js. The MainNavigation.js sets up a StackNavigator with two screens, Screen1.js and Screen2.js. Screen1 has been set as the initial screen for our StackNavigator.
Navigating between screens
We can navigate from Screen1 to Screen2 by using
this.props.navigation.navigate('Screen2');
and we can go back to Screen1 from Screen2 by using
this.props.navigation.goBack();
So code in Screen1 becomes
export default class Screen extends React.Component {
render() {
return (
<View style={styles.container}>
<Button title={'Go to screen 2'} onPress={() => this.props.navigation.navigate('Screen2')} />
</View>
)
}
}
And code in Screen2 becomes:
export default class Screen extends React.Component {
render() {
return (
<View style={styles.container}>
<Button title={'Go back'} onPress={() => this.props.navigation.goBack()} />
</View>
)
}
}
Now we can navigate between Screen1 and Screen2
Sending values from Screen1 to Screen2
To send a value between Screen1 and Screen2, two steps are involved. First we have to send it, secondly we have to capture it.
We can send a value by passing it as a second parameter. Notice how the text value is contained in an object.
this.props.navigation.navigate('Screen2', {text: 'Hello from Screen 1' });
And we can capture it in Screen2 by doing the following, the first value in getParams is the key the second value is the default value.
const text = this.props.navigation.getParams('text','nothing sent');
So Screen1 now becomes
export default class Screen extends React.Component {
render() {
return (
<View style={styles.container}>
<Button
title={'Go to screen 2'}
onPress={() => this.props.navigation.navigate('Screen2', {
text: 'Hello from screen 1'
})} />
</View>
)
}
}
And code in Screen2 becomes:
export default class Screen extends React.Component {
render() {
const text = this.props.navigation.getParam('text', 'nothing sent')
return (
<View style={styles.container}>
<Text>{text}</Text>
<Button
title={'Go back'}
onPress={() => this.props.navigation.goBack()} />
</View>
)
}
}
Sending values from Screen2 back to Screen1
The easiest way I have discovered to send a value from Screen2 to Screen1 is to pass a function to Screen2 from Screen1 that will update the state in Screen1 with the value that you want to send
So we can update Screen1 to look like this. First we set an initial value in state. Then we create a function that will update the state. Then we pass that function as a parameter. We will display the captured value from Screen2 in a Text component.
export default class Screen1 extends React.Component {
state = {
value: ''
}
receivedValue = (value) => {
this.setState({value})
}
render() {
return (
<View style={styles.container}>
<Button
title={'Go to screen 2'}
onPress={() => this.props.navigation.navigate('Screen2', {
text: 'Hello from Screen 1',
receivedValue: this.receivedValue }
)} />
<Text>{this.state.value}</Text>
</View>
)
}
}
Notice that we are passing the function receivedValue in the same way that we passed the text earlier.
Now we have to capture the value in Screen2 and we do that in a very similar way that we did previously. We use getParam to get the value, remembering to set our default. Then when we press our Go back button we update it to call the receivedValue function first, passing in the text that we want to send back.
export default class Screen2 extends React.Component {
render () {
const text = this.props.navigation.getParam('text', 'nothing sent');
const receivedValue = this.props.navigation.getParam('receivedValue', () => {});
return (
<View style={styles.container}>
<Button
title={'Go back'}
onPress={() => {
receivedValue('Hello from screen 2')
this.props.navigation.goBack()
}} />
<Text>{text}</Text>
</View>
);
}
}
Alternatives to using getParam
It is possible to not use the getParam method and instead access the values directly. If we were to do that we would not have the option of setting a default value. However it can be done.
In Screen2 we could have done the following:
const text = this.props.navigation.state.params.text;
const receivedValue = this.props.navigation.state.params.receivedValue;
Capturing values in lifecycle events (Screen1 to Screen2)
react-navigation allows you to capture values using the lifecycle events. There are a couple of ways that we can do this. We could use NavigationEvents or we could use listeners set in the componentDidMount
Here is how to set it up using NavigationEvents
import React, {Component} from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { NavigationEvents } from 'react-navigation'; // you must import this
export default class Screen2 extends React.Component {
state = {
text: 'nothing passed'
}
willFocusAction = (payload) => {
let params = payload.state.params;
if (params && params.value) {
this.setState({value: params.value});
}
}
render() {
return (
<View style={styles.container}>
<NavigationEvents
onWillFocus={this.willFocusAction}
/>
<Text>Screen 2</Text>
<Text>{this.state.text}</Text>
</View>
)
}
}
Here is how to do it using listeners in the componentDidMount
export default class Screen2 extends React.Component {
componentDidMount () {
// we add the listener here
this.willFocusSubscription = this.props.navigation.addListener('willFocus', this.willFocusAction);
}
componentWillUmount () {
// we remove the listener here
this.willFocusSubscription.remove()
}
state = {
text: 'nothing passed'
}
willFocusAction = (payload) => {
let params = payload.state.params;
if (params && params.value) {
this.setState({value: params.value});
}
}
render() {
return (
<View style={styles.container}>
<Text>Screen 2</Text>
<Text>{this.state.text}</Text>
</View>
)
}
}
Passing navigation via components
In the above examples we have passed values from screen to screen. Sometimes we have a component on the screen and we may want to navigate from that. As long as the component is used within a screen that is part of a navigator then we can do it.
If we start from our initial template and construct two buttons. One will be a functional component the other a React component.
MyButton.js
// this is a functional component
import React, {Component} from 'react';
import { View, StyleSheet, Text, TouchableOpacity } from 'react-native';
export const MyButton = ({navigation, value, title}) => {
return (
<TouchableOpacity onPress={() => navigation.navigate('Screen2', { value })}>
<View style={styles.buttonStyle}>
<Text>{title}</Text>
</View>
</TouchableOpacity>
)
}
const styles = StyleSheet.create({
buttonStyle: {
width: 200,
height: 60,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'red'
}
});
MyOtherButton.js
// this is a React component
import React, {Component} from 'react';
import { View, StyleSheet, Text, TouchableOpacity } from 'react-native';
export default class MyOtherButton extends React.Component {
render() {
const { navigation, value, title } = this.props;
return (
<TouchableOpacity onPress={() => navigation.navigate('Screen2', { value })}>
<View style={styles.buttonStyle}>
<Text>{title}</Text>
</View>
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
buttonStyle: {
width: 200,
height: 60,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'yellow'
}
});
Regardless of the type of component, notice that navigation is a prop. We must pass navigation to the component otherwise it will not work.
Screen1.js
import React, {Component} from 'react';
import { View, StyleSheet, Text, Button } from 'react-native';
import { MyButton } from './MyButton';
import MyOtherButton from './MyOtherButton';
export default class Screen1 extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Screen 1</Text>
<MyButton
title={'Press my button'}
navigation={this.props.navigation}
value={'this is a string passed using MyButton'}
/>
<MyOtherButton
title={'Press my other button'}
navigation={this.props.navigation}
value={'this is a string passed using MyOtherButton'}
/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
}
});
Notice in Screen1.js as it is contained in a StackNavigator it will have access to this.props.navigation. We can pass that through to our component as a prop. As long as we use that in our component then we should be able to navigate by using the components own functionality.
<MyButton
title={'Press my button'}
navigation={this.props.navigation} // pass the navigation here
value={'this is a string passed using MyButton'}
/>
Snacks
Here is a snack for passing params.
Here is a snack for passing params and capturing in lifecycle events.
Here is a snack passing navigation to components
1) On Home Screen:-
Initialise:-
constructor(props) {
super(props);
this.navigate = this.props.navigation.navigate; }
Send:-
this.navigate("DetailScreen", {
name: "Detail Screen",
about:"This is Details Screen Page"
});
2) On Detail Screen:-
Initialise:-
constructor(props) {
super(props);
this.params = this.props.navigation.state.params;
}
Retrive data:-
console.log(this.params.name);
console.log(this.params.about);
const {navigate} = this.props.navigation;
…
.
.
{ text: 'Done', onPress:() => {
navigate('homeScreen',...params);}
You can get those params like
const {params} = this.props.navigation.state
HomeScreen.js
this.props.navigation.navigate('Screen2',{ user_name: 'aaa',room_id:'100' });
Screen2.js
const params = this.props.route.params;
user_name = params.user_name;
room_id = params.room_id
You can easily send and receive your params with react-navigation like below
Send params:
{
text: 'Done',
onPress: () => {
this.props.navigation.navigate(
HomeScreen,
{param1: 'value1', param2: 'value2'}
);
}
}
Get params in HomeScreen:
const { navigation } = this.props;
var param1 = navigation.getParam('param1', 'NO-VALUE');
var param2 = navigation.getParam('param2', 'NO-VALUE');
the 'NO-VALUE' is default value, if there is not desired param
I am assuming that you are using react-navigation. So, in react-navigation we can pass data in two pieces:
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 */ })
Read the params in your screen component:
this.props.navigation.getParam(paramName, someDefaultValue)
Alert Button
<Button
title="Alert View"
onPress={() => {
this.props.navigation.navigate('alerts', {
itemId: 86,
otherParam: 'anything you want here',
});
}}
/>
Screen:
const itemId = navigation.getParam('itemId', 'NO-ID');
const otherParam = navigation.getParam('otherParam', 'some default value')
Screen 1:
<Button title="Go Next"
onPress={() => navigation.navigate('SecondPage', { paramKey: userName })} />
Screen 2:
const SecondPage = ({route}) => {
....
....
<Text style={styles.textStyle}>
Values passed from First page: {route.params.paramKey}
</Text>
....
....
}

react-native componentWillUnmount not working while navigating

I am doing this simple steps but unmount was not calling I don't know why. Please I need a solution for this I need unmount to be get called while navigating to another screen...
class Homemain extends Component {
constructor(props) {
super(props);
}
componentWillMount(){
alert('willMount')
}
componentDidMount(){
alert('didMount')
}
componentWillUnmount(){
alert('unMount')
}
Details = () => {
this.props.navigation.navigate('routedetailsheader')
}
render() {
return(
<View style={styles.container}>
<TouchableOpacity onPress={() => this.Details()} style={{ flex: .45, justifyContent: 'center', alignItems: 'center', marginTop: '10%', marginRight: '10%' }}>
<Image
source={require('./../Asset/Images/child-notification.png')}
style={{ flex: 1, height: height / 100 * 20, width: width / 100 * 20, resizeMode: 'contain' }} />
<Text
style={{ flex: 0.5, justifyContent: 'center', fontSize: width / 100 * 4, fontStyle: 'italic', fontWeight: '400', color: '#000', paddingTop: 10 }}>Details</Text>
</TouchableOpacity>
</View>
);
}
}
export default (Homemain);
This is my RouteConfiguration in this way I am navigating to the next screen. Can someone please help me for this error so that i can proceed to the next steps
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { addNavigationHelpers, NavigationActions } from 'react-navigation';
import { connect } from 'react-redux';
import { BackHandler } from 'react-native';
import { Stack } from './navigationConfiguration';
const getCurrentScreen = (navigationState) => {
if (!navigationState) {
return null
}
const route = navigationState.routes[navigationState.index]
if (route.routes) {
return getCurrentScreen(route)
}
return route.routeName
}
class StackNavigation extends Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
navigation: PropTypes.shape().isRequired,
};
constructor(props) {
super(props);
BackHandler.addEventListener('hardwareBackPress', this.backAction);
}
//backAction = () => this.navigator.props.navigation.goBack();
backAction = () => {
const { dispatch, navigation } = this.props;
const currentScreen = getCurrentScreen(navigation)
if (currentScreen === 'Homemain') {
return false
}
else
if (currentScreen === 'Login') {
return false
}
dispatch(NavigationActions.back());
return true;
};
render() {
const { dispatch, navigation } = this.props;
return (
<Stack
ref={(ref) => { this.navigator = ref; }}
navigation={
addNavigationHelpers({
dispatch,
state: navigation,
})
}
/>
);
}
}
export default connect(state => ({ navigation: state.stack }))(StackNavigation);
Routing to a new screen does not unmount the current screen.
For you usecase you instead of writing the code in componentWillUnmount you can continue by writing it after calling navigate in Details itself.
If you are looking for a callback when you press back from the new screen to come back to the current screen. Use goBack as shown in https://github.com/react-navigation/react-navigation/issues/733
If you are using a stack navigator, then routing to a new view loads the new view above the old one. The old view is still there for when you navigate back.
As I understand from your question and code you are using redux with navigation and want to unmount a screen. So what I did I just added a screen component inside another component to make my screen component as child.
e.g. below is the snippet that I am using to unmount the PushScreen from PushedData component.
I render PushScreen and inside it there is component PushedData that originally making the view. On PushedData `componentWillMount I am just doing some conditional functionality and on success I am just unmounting PushData from PushScreen.
class PushScreen extends Component{
state ={ controllerLaunched: false };
updateControllerLauncher = () => {
this.setState({ controllerLaunched: true });
}
render (){
if(this.state.controllerLaunched){
return null;
} else {
return <PushedData handleControllerLauncher={this.updateControllerLauncher} />;
}
}
}
class PushedData extends Component{
componentWillMount(){
this.unmountPushData();//calling this method after some conditions.
}
unmountPushData = () => {
this.props.handleControllerLauncher();
}
render(){
return (
<View><Text>Component mounted</Text></View>
);
}
}
Let me know if you need more information.
When you use Stack Navigator then routing to a new view loads the new view above the old one as Rob Walker said. There is a workaround. You can bind blur event listener on componentDidMount using navigation prop:
componentDidMount() {
this.props.navigation.addListener('blur', () => {
alert('screen changed');
})
}
So when your screen goes out of focus the event listener is called.
You can find more about events right here.
If you want to go next Screen use (.replace instead .navigate) where you want to call componentWillUnmount. and if you want to go back to one of previous screens use .pop or .popToTop.
you can set condition for costume function that u write for hardware button , for example when ( for example for React Native Router Flux ) Actions.currentScene === 'Home' do something or other conditions u want .