I am trying to go back to previous view in my react native app using this code
'use strict';
var React = require('react-native');
var {
AppRegistry,
Component,
StyleSheet,
Text,
View,
BackAndroid,
Navigator
} = React;
var HomePage = require('./HomePage');
class DetailsPage extends Component{
constructor(props){
super(props);
}
render(){
return(
<View style={styles.container}>
<Text style={styles.text}>
{this.props.description}
</Text>
</View>
)
}
}
BackAndroid.addEventListener('hardwareBackPress', function() {
this.props.navigator.pop(); // line 32
return true;
});
var styles = StyleSheet.create({
container: {
flex: 1
},
text:{
color:'#000',
textAlign:'center',
fontWeight:'bold',
flex:1,
fontSize:20
},
});
module.exports = DetailsPage;
While debugging I see that the back event is detected successfully but it crashes at this line this.props.navigator.pop() giving me this error.
Cannot read property 'props' of undefinedhandleException #
D:\React\Kora\node_modules\react-native\Libraries\JavaScriptAppEngine\Initialization\ExceptionsMana…:61handleError
#
D:\React\Kora\node_modules\react-native\Libraries\JavaScriptAppEngine\Initialization\InitializeJava…:80ErrorUtils.reportFatalError
#
D:\React\Kora\node_modules\react-native\packager\react-packager\src\Resolver\polyfills\error-guard.…:28guard
#
D:\React\Kora\node_modules\react-native\Libraries\Utilities\MessageQueue.js:43callFunctionReturnFlushedQueue
#
D:\React\Kora\node_modules\react-native\Libraries\Utilities\MessageQueue.js:86onmessage
# debuggerWorker.js:39
my guess it that this.props can not be accessed out side the class braces but I don't know how to overcome this problem. if I put the BackAndroid.addEventListener inside the class it gives me error
UnExpectedToken
edit: BackAndroid is now deprecated. You should probably be using BackHandler instead.
In your event, this does not refer to what you think it refers to.
In this case, it refers to the object on which the event is called.
The way I did it in my react-native app, was by adding the event in the componentDidMount()-function of my main component (the component in which the Navigator is rendered).
I added it there the (sort of) following way:
class ViewComponent extends Component {
...
componentDidMount() {
//the '.bind(this)' makes sure 'this' refers to 'ViewComponent'
BackAndroid.addEventListener('hardwareBackPress', function() {
this.props.navigator.pop();
return true;
}.bind(this));
}
...
}
It should work like this in your project.
The componentDidMount() is fired right after the initial render. You could probably also use componentWillMount(). So it adds the event right after the first time it renders. It only gets fired off once, so there are no overlapping events and stuff like that.
I wouldn't, however, add the event listener on scenes. It brings the risk of perhaps adding the event twice. Although I'm not sure if adding that event twice would actually change anything.
Related
import { View, ScrollView, StyleSheet } from 'react-native'
import Heading from './Heading'
import Input from './Input'
class App extends Component {
constructor () {
super()
this.state = {
input value: '',
todos: [],
type: 'All'
}
inputChange (inputValue) {
this.setState({inputValue})
}
}
render () {
const {todos, inputValue, type } = this.state
return (
<View style={styles.container}>
<ScrollView keyboardShouldPersistTaps='always'
style={styles.content}>
<Heading />
<Input
input Value={inputValue}
inputChange={(text) => this.inputChange(text)} />
</ScrollView>
</View>
)
}
}
const styles = StyleSheet.create ({
container: {
flex: 1,
backgroundColor: '#f5f5f5'
},
content: {
flex: 1,
paddingTop: 60
}
})
export default App
"So I have this error that I have tried to fix through disabled eslint and it's not working. This is my first time using react-native. I'm taking a class and this is for an assignment of mine to create a TodoApp. My teacher gave us the exact code he wanted us to input into VSCode, so we could then make changes to it accordingly for the assignment. I was halfway through his video, and this error kept coming up, but not for him. I have looked all around for an answer and can't seem to find one. The section of code that gives me a problem in the inputChange (inputValue) and say it's missing a semicolon. In the video the teacher doesn't put one and it works completely fine. I've done some reading that it could be eslint, but I've tried to disable it and it still doesn't work."
With this code, you are attempting to define the method inputChange inside the constructor method, which is not possible. You need to close the constructor method definition before defining your inputChange method. I recommend taking a closer look at the code your instructor provided. I suspect that on closer inspection you will find one curly brace closing the setState function and another curly brace closing the constructor method before that inputChange method definition starts.
change position of inputChange function from inside of constructor to body of class ( after closing constructor )
class App extends Component {
constructor() {
// ...
}
inputChange(inputValue) {
// ...
}
render() {
return (
<>{/* rest of code */}</>
);
}
}
Edit
you may name properties incorrect
I am new to react native / ignite bowser. I am making a react native / ignite bowser project.
In my app first there is a welcome screen which appears when the app starts. There is a 'continue' button in the welcome screen. When I click the 'continue' button it should go to the login screen. But it shows this error:
TypeError: undefined is not an object (evaluating 'navigation.navigate')
I am running the app on an actual physical android device.
Can anyone help me? This is my code in github:
https://github.com/boidurja/smartcope_new.git
This is the part of the code where the error occurs:
<Button title="Countinue" containerStyle={{ flex: 1 }} onPress={ ( ) => navigation.navigate('login') }/>
in this file:
app/screens/auth-screens/welcome-screen.tsx
I was told that in this case the issue is that the props property doesn't have the navigation object, coz in new version of the library we have to use react hooks for that check for react-navigation v5 documentation on how to access the navigation prop in react component. I tried it but was not successful.
I see this error
You don't seem to receive the Navigation object properly. If this is the component you use, use the useNavigation function.
useNavigation is a hook which gives access to navigation object.
It's useful when you cannot pass the navigation prop into the
component directly, or don't want to pass it in case of a deeply
nested child.
import { useNavigation } from '#react-navigation/native';
export const AuthScreensWelcomeScreen: FunctionComponent<AuthScreensWelcomeScreenProps> = observer((props) => {
...
const navigation = useNavigation();
...
<Button title="Countinue" containerStyle={{ flex: 1 }} onPress={ ( ) => navigation.navigate('login') }/>
OR
You can get access to the root navigation object through a ref and
pass it to the RootNavigation which we will later use to navigate.
Usage
import { NavigationContainer } from '#react-navigation/native';
import { navigationRef } from './RootNavigation';
export default function App() {
return (
<NavigationContainer ref={navigationRef}>{/* ... */}</NavigationContainer>
);
}
// RootNavigation.js
import * as React from 'react';
export const navigationRef = React.createRef();
export function navigate(name, params) {
navigationRef.current?.navigate(name, params);
}
// add other navigation functions that you need and export them
// any js module
import * as RootNavigation from './path/to/RootNavigation.js';
export const AuthScreensWelcomeScreen: FunctionComponent<AuthScreensWelcomeScreenProps> = observer((props) => {
// ...
<Button title="Countinue" containerStyle={{ flex: 1 }} onPress={ ( ) => RootNavigation.navigate('login') }/>
In welcomscreen.tsx, try changing this line:
const {
navigation
} = props;
To this:
const {
navigation
} = this.props;
Alternately, you could leave that snippet out and alter the button snippet like this:
<Button title="Countinue" containerStyle={{ flex: 1 }} onPress={() => this.props.navigation.navigate('login') }
Hope that helps.
My application scenario is like, let say you have three components:
class ComponentOne extends Component {
render() {
return (
<View>
<Text>Component One</Text>
<Button
title='Go To Component Two'
onPress={() => this.props.navigation.navigate('two')}/>
</View>
);
}
}
class ComponentTwo extends Component {
render() {
return (
<View>
<Text>Component Two</Text>
<Button
title='Go To Component Three'
onPress={() => this.props.navigation.navigate('three')}/>
</View>
);
}
}
class ComponentThree extends Component {
render() {
return (
<View>
<Text>Component Three</Text>
<Button
title='Go To Component One'
onPress={() => this.props.navigation.navigate('one')}/>
</View>
);
}
}
export default createStackNavigator({one: ComponentOne,two: ComponentTwo,three: ComponentThree});
Now when I load the app the ComponentOne will be loaded, inside the ComponentOne when I click on the button Go To Component Two it will navigate me to the ComponentTwo, inside ComponentTwo when I click on the button Go To Component Three it will navigate me to the ComponentThree and so on. Now let say I am in ComponentTwo and on the same time I close the application in the phone and I open the app switcher and clean all the running apps and load the same app again, so, it will be again loaded with ComponentOne.
My question is how to program the react navigation to continue from the same component where last time I left, even after cleaning the app from a background (cleaning all apps in app switcher)?
Is there any builtin way in react navigation to do this? Can anyone tell? Examples will be more appreciated. Thanks!
All Navigators have onNavigationStateChange where you can handle the navigation state changing. Example code:
import React from 'react';
import { AsyncStorage } from 'react-native';
import { createStackNavigator, NavigationActions } from 'react-navigation';
const Navigator = createStackNavigator({
ComponentOne: {
screen: ComponentOne,
},
ComponentTwo: {
screen: ComponentTwo,
},
ComponentThree: {
screen: ComponentThree,
},
}, {
initialRouteName: 'ComponentOne',
});
class App extends Component {
constructor(props) {
this.navigator = React.createRef();
}
componentDidMount() {
try {
// Retrieve the last route
const value = AsyncStorage.getItem('lastNavigationRoute').then((result) => {
if (result) {
this.navigator.current.dispatch(NavigationActions.navigate({
routeName: result,
}));
}
});
} catch (e) {
// handle the error
}
}
handleStateChange = (previousState, nextState) => {
// Here we get the Navigate action type only
const navigateAction = NavigationActions.navigate({ routeName: 'dummyRoute' });
if (action.type === navigateAction.type) {
try {
// Saving the last route
AsyncStorage.setItem('lastNavigationRoute', nextState.routeName);
} catch (e) {
// handle the error
}
}
}
render() {
// You could also set a state with loader to handle loading from AsyncStorage
return (
<Navigator onNavigationStateChange={this.handleStateChange} ref={this.navigator} />
);
}
}
How it works:
On every navigation state changing you also save the last routeName
from Navigate action
When component did mount, you check for saved
route in AsyncStorage
If there is a route, you dispatch the navigate action (it's possible to implement replace action as well)
Hope it helps.
i dont think that there is a solution directly using react-navigation.
What i think you could do is to save a value of the current screen to the storage of the phone and then use this value on app start to detect which screen to show
I am using stackNavigator for navigating between screens. I am calling two API's in componentDidMount() function in my second activity. When i load it first time, it gets loaded successfully. Then i press back button to go back to first activity. Then, if i am again going to second activity, the APIs are not called and I get render error. I am not able to find any solution for this. Any suggestions would be appreciated.
If anyone coming here in 2019, try this:
import {NavigationEvents} from 'react-navigation';
Add the component to your render:
<NavigationEvents onDidFocus={() => console.log('I am triggered')} />
Now, this onDidFocus event will be triggered every time when the page comes to focus despite coming from goBack() or navigate.
If the upvoted syntax that uses NavigationEvents component is not working, you can try with this:
// no need to import anything more
// define a separate function to get triggered on focus
onFocusFunction = () => {
// do some stuff on every screen focus
}
// add a focus listener onDidMount
async componentDidMount () {
this.focusListener = this.props.navigation.addListener('didFocus', () => {
this.onFocusFunction()
})
}
// and don't forget to remove the listener
componentWillUnmount () {
this.focusListener.remove()
}
The React-navigation documentation explicitly described this case:
Consider a stack navigator with screens A and B. After navigating to
A, its componentDidMount is called. When pushing B, its
componentDidMount is also called, but A remains mounted on the stack
and its componentWillUnmount is therefore not called.
When going back from B to A, componentWillUnmount of B is called, but
componentDidMount of A is not because A remained mounted the whole
time.
Now there is 3 solutions for that:
Subscribing to lifecycle events
...
React Navigation emits events to screen components that subscribe to
them. There are four different events that you can subscribe to:
willFocus, willBlur, didFocus and didBlur. Read more about them in the
API reference.
Many of your use cases may be covered with the withNavigationFocus
higher-order-component, the <NavigationEvents /> component, or the
useFocusState hook.
the withNavigationFocus
higher-order-component
the <NavigationEvents />
component
the useFocusState hook (deprecated)
withNavigationFocus
higher-order-component
import React from 'react';
import { Text } from 'react-native';
import { withNavigationFocus } from 'react-navigation';
class FocusStateLabel extends React.Component {
render() {
return <Text>{this.props.isFocused ? 'Focused' : 'Not focused'}</Text>;
}
}
// withNavigationFocus returns a component that wraps FocusStateLabel and passes
// in the navigation prop
export default withNavigationFocus(FocusStateLabel);
<NavigationEvents /> component
import React from 'react';
import { View } from 'react-native';
import { NavigationEvents } from 'react-navigation';
const MyScreen = () => (
<View>
<NavigationEvents
onWillFocus={payload => console.log('will focus', payload)}
onDidFocus={payload => console.log('did focus', payload)}
onWillBlur={payload => console.log('will blur', payload)}
onDidBlur={payload => console.log('did blur', payload)}
/>
{/*
Your view code
*/}
</View>
);
export default MyScreen;
useFocusState hook
First install library yarn add react-navigation-hooks
import { useNavigation, useNavigationParam, ... } from 'react-navigation-hooks'
function MyScreen() { const focusState = useFocusState(); return <Text>{focusState.isFocused ? 'Focused' : 'Not Focused'}</Text>; }
Here is my personal solution, using onDidFocus() and onWillFocus() to render only when data has been fetched from your API:
import React, { PureComponent } from "react";
import { View } from "react-native";
import { NavigationEvents } from "react-navigation";
class MyScreen extends PureComponent {
state = {
loading: true
};
componentDidMount() {
this._doApiCall();
}
_doApiCall = () => {
myApiCall().then(() => {
/* Do whatever you need */
}).finally(() => this.setState({loading: false}));
};
render() {
return (
<View>
<NavigationEvents
onDidFocus={this._doApiCall}
onWillFocus={() => this.setState({loading: true})}
/>
{!this.state.loading && /*
Your view code
*/}
</View>
);
}
}
export default MyScreen;
Solution for 2020 / React Navigation v5
Inside your screen's ComponentDidMount
componentDidMount() {
this.props.navigation.addListener('focus', () => {
console.log('Screen.js focused')
});
}
https://reactnavigation.org/docs/navigation-events/
Alternatively: Put the addListener method in constructor instead to prevent duplicated calls
React-navigation keeps the component mounted even if you navigate between screens. You can use the component to react to those events :
<NavigationEvents
onDidFocus={() => console.log('hello world')}
/>
More info about this component here.
According to react-navigation docs we can use as below
componentDidMount () {
this.unsubscribe= this.props.navigation.addListener('focus', () => {
//Will execute when screen is focused
})
}
componentWillUnmount () {
this.unsubscribe()
}
Similar to vitosorriso`s answer but should changed didFocus to focus according to docs
You want to perform something after every time navigating to a component using drawernavigator or stacknavigator ; for this purpose NavigationEvents fits better than componentDidMount .
import {NavigationEvents} from "react-navigation";
<NavigationEvents onDidFocus={()=>alert("Hello, I'm focused!")} />
Note : If you want to do the task every time after focusing on a component using drawer navigation or stack navigation then using NavigationEvents is better idea. But if you want to do the task once then you need to use componenetDidMount .
//na pagina que você quer voltar
import {NavigationEvents} from 'react-navigation';
async atualizarEstado() {
this.props.navigation.setParams({
number: await AsyncStorage.getItem('count'),
});}
render() {
return (
<View style={styles.container}>
<NavigationEvents onDidFocus={() => this.atualizarEstado()} />
</View>
);
}
I have face this issue, the problem is when you navigate a page, the first time it call constructor, componentWillmount, render componentDidmount,
but in second time when navigate to the same page it only call render, so if you do any API call or something from componentDidmount it would not be called,
and also componentWillunmount never called.
You can use this method, if you are using react-navigation 5.x with class component, it can solve your problem.
for every class component page add this method and call this method once from the constructor
constructor(props) {
super(props);
this.state = {
...
};
...
this.navigationEventListener(); // call the function
}
navigationEventListener = () => { // add this function
let i = 0;
const initialState = this.state;
this.props.navigation.addListener('focus', () => {
if (i > 0) {
this.setState(initialState, () => {
//this.UNSAFE_componentWillMount(); // call componentWillMount
this.componentDidMount(); // call componentDidMount
});
}
});
this.props.navigation.addListener('blur', () => {
this.componentWillUnmount(); //call componentWillUnmount
++i;
});
}
https://reactnavigation.org/docs/navigation-events/
useEffect(() => {
const unsubscribe = props.navigation.addListener('focus', () => {
// do something
// Your apiCall();
});
return unsubscribe;
}, [props.navigation]);
In React, componentDidMount is called only when component is mounted.I think what you are trying to do is call your API on going back in StackNavigator. You can pass a callback function as parameter when you call navigate like this on Parent Screen:
navigate("Screen", {
onNavigateBack: this.handleOnNavigateBack
});
handleOnNavigateBack = () => {//do something};
And on Child Screen
this.props.navigation.state.params.onNavigateBack();
this.props.navigation.goBack();
Hi I am trying to navigate to next component using navigate function. I am using react-navigation for the navigation among multiple components.
Suppose I have index.android.js and DashboardScreen.js component. I am trying to navigate to DashboardScreen.js component from index component.
It is navigating but index component always retain in component stack. when I press back then it opens index.android.js which should not be. Does anyone know how to manage this in react-native. In Android, finish() works for this.
navigate("DashboardScreen");
When I am navigating from SplashScreen to EnableNotification then SplashScreen should be destroyed, if I am navigating from EnableNotification to CreateMessage then EnableNotification should be destroyed and if I am navigating from CreateMessage to DashboardScreen then CreateMessage should be destroyed. As of now no component is being destroyed.
index.android.js
class SplashScreen extends Component {
render() {
if (__DEV__) {
console.disableYellowBox = true;
}
const { navigate } = this.props.navigation;
AsyncStorage.getItem("#ProductTour:key").then(value => {
console.log(value);
if (value) {
navigate("DashboardScreen");
}
});
return (
....
);
}
}
const App = StackNavigator(
{
Splash: {
screen: SplashScreen,
navigationOptions: {
header: {
visible: false
}
}
},
EnableNotification: {
screen: EnableNotificationScreen,
navigationOptions: {
header: {
visible: false
}
}
},
CreateMessage: {
screen: CreateMessageScreen,
navigationOptions: {
header: {
visible: false
}
}
},
DashboardScreen: {
screen: DashboardScreen,
navigationOptions: {
header: {
visible: false
}
}
}
},
{
initialRouteName: "Splash"
}
);
Just use 'replace' in place of 'navigate'
this.props.navigation.replace('Your Next Component Name')
First of all, using AsyncStorage in an a synchronous function (most especially a lifecycle one) is such a bad idea. You should typically keep ASyncStorage to places in your folder / app structure that make sense for where you access/keep data but since that's not the question I will just mention it quickly here...
Basically you are asking to navigate once the ASync method completes itself based on EVERY render... Those new to RN should know that an awful lot of things can cause a render to fire. Some cases, the render function can fire (I have seen this many times before) 10 or more times before finalizing the last render. This means you would have fired that ASyncStorage method 10 times... definitely something to think about when implementing this stuff. So more or less, the .then(); part of the AsyncStorage function is firing long after the render has already finished doing it's thing. If it was a reasonable approach to use I would say to put the return part of the render function inside of the .then((value) => { return ( ... ); });. But this is an even worse idea. Basically you need the right lifecycle method here and it's NOT the render method.
Anyway, since I have never used this component library before I can only help nudge you in the right direction so here goes... These docs on their webpage seem to say that you need a reference to the props navigator passed down to the component in which you are using it. So if you created the navigator in this class, you would use this.refs.whateverYouNamedTheNavigatorReference.navigate('SomeItemName'). If you are in the class that has been passed this navigator as a prop, you use this.props.passNavigatorPropName.navigate('SomeItemName'). I see you are using variable deconstruction to get the navigate callback but I would caution on doing this, this way because I have seen it cause errors by grabbing an old version of the navigate function or its parent reference by accident and causing a cascading error effect.
Also, if you are going to be using ASyncStorage in a component file (again, would recommend putting this in a component/class where your data is accessed throughout the app...) and you are going to use it to decide the app should navigate forwards/backwards... definitely remove it from the render function and put it in maybe the constructor, componentWillReceiveProps, componentDidReceiveProps or componentWillUpdate lifecycle functions. That way it fires based on an update, a new passed prop obj or one time as the component is built. Anything is better than firing it every single render.
Lastly, I do not know what you have setup for your StackNavigator route stack object but you would need to have the keyword you used "DashboardScreen" in there pointing to an actual component that has been imported properly. The "DashboardScreen" keyword most likely would connect in your StackNavigator object to some component import like so...
import Dashboard from '../Views/DashboardScreenView';
StackNavigator({
DashboardScreen: {
screen: Dashboard,
path: 'dashboard/:main',
navigationOptions: null,
},
});
There is a simple way here: use "replace" (reference link repleace in navigation ,For example, you are at the screen "Login" ,
and you want to move to screen "Home", insert this code in screen "Login"
<TouchableOpacity onPress={() => { this.login() }}>
<Text}>Click me to Login</Text>
</TouchableOpacity>
and method login:
login(){
this.props.navigation.replace('Home')
}
Screen "Login" will be replaced by "Home", in Android, press Back Button =>app exit, no back screen "Login"
Based on your requirement, i suggest following setup:
SplashNavigator.js
const SplashNavigator = StackNavigator({
Splash: {
screen: SplashScreen,
navigationOptions: {
header: {
visible: false
}
}
}
});
AppNavigator.js
const AppNavigator = StackNavigator(
{
EnableNotification: {
screen: EnableNotificationScreen,
navigationOptions: {
header: {
visible: false
}
}
},
CreateMessage: {
screen: CreateMessageScreen,
navigationOptions: {
header: {
visible: false
}
}
},
Dashboard: {
screen: DashboardScreen,
navigationOptions: {
header: {
visible: false
}
}
}
},
{
initialRouteName: "EnableNotification"
}
);
In your index.android.js, you will render the SplashNavigator.
The SplashNavigator will render the SplashScreen. It has initial state value isReady set to false, so it will render a loading text until the #ProductTour:key value from AsyncStorage is loaded (AsyncStorage is async function, u should not put it in your render function). It will then render your AppNavigator and render your EnableNotification as initial route.
class SplashScreen extends Component {
constructor() {
super(props);
this.state = {
isReady: false,
}
}
componentDidMount() {
AsyncStorage.getItem("#ProductTour:key").then(value => {
console.log(value);
// you will need to handle case when `#ProductTour:key` is not exists
this.setState({
isReady: true,
});
});
}
render() {
const { isReady } = this.state;
return (
<View style={{flex: 1}}>
{
isReady ?
<AppNavigator />
: <Text>Loading</Text>
}
</View>
);
}
}
Then on EnableNotificationScreen and CreateMessageScreen, change your navigate route function to use NavigationActions.reset from doc
Example:
import { NavigationActions } from 'react-navigation';
handleOnPressButton = () => {
const resetAction = NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: "CreateMessage" })
]
});
this.props.navigation.dispatch(resetAction);
}
Yes in react native you can finish the current screen before navigating to new screen with the help of NavigationActions . Please refer this link -
http://androidseekho.com/others/reactnative/finish-current-screen-on-navigating-another-in-react-native/
SplashNavigator.js
const SplashNavigator = StackNavigator({
Splash: {
screen: SplashScreen,
navigationOptions: {
header: null}
}
}
});
Import StackActions and NavigationActions from react-navigation.
import { StackActions, NavigationActions } from 'react-navigation';
below code for performing Action
navigateToHomeScreen = () => {
const navigateAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: "HomeScreen" })],
});
this.props.navigation.dispatch(navigateAction);
}