I am trying to validate a text field on click of Submit button. But I always get the error -
TypeError: undefined is not an object (evaluating 'props.deviceLocale') on my emulator.
(I am using an Android emulator).
However I am nowhere using 'deviceLocale' in my code. I am not aware if it is required for anything I have in my code.
This is my code:
import React, {Component} from 'react';
import { View, Text, TouchableOpacity, TextInput } from 'react-native';
import ValidationComponent from 'react-native-form-validator';
export default class App extends ValidationComponent {
constructor() {
super()
this.state = {
name: "abc"
}
}
_onPressButton() {
this.validate({
name: {minlength: 3, maxlength: 7, required: true},
});
}
render() {
return (
<View>
<TextInput
ref = "name"
onChangeText = {(name) => this.setState({name})}
value = {this.state.name}
/>
<TouchableOpacity onPress = {this._onPressButton}>
<Text>Submit</Text>
</TouchableOpacity>
</View>
)
}
}
Error snap:
You are getting the error even before loading of the component then, do bind your _onPressButton correctly, then atleast your component will get mounted properly, then then your upcoming errors will follow like the use of this.validate is somewhat ambiguous to me as I cannot see validate function in the component.
To bind your _onPressed, declare it like below:
_onPressButton = () => {
this.validate({
name: {minlength: 3, maxlength: 7, required: true},
});
}
The error is causing as _onPressed is getting called as soon as your component is getting mounted. Let me know in comments if this helps you getting ahead with your component mounting atleast.
Edited:
Also, your constructor doesn't provide props to the super constructor,
Declare it like below:
constructor(props) {
super(props);
this.state = {
name: "abc"
}
}
I want the name of the current route or screen in react-navigation which I want to use inside if condition to make some changes.
For react-navigation v5:
import { useRoute } from '#react-navigation/native';
const route = useRoute();
console.log(route.name);
You can catch it as the following code:
this.props.navigation.state.routeName
If you are using nested navigators, you can use this code to get current active screen's state
import { NavigationState } from 'react-navigation';
const getActiveRouteState = function (route: NavigationState): NavigationState {
if (!route.routes || route.routes.length === 0 || route.index >= route.routes.length) {
return route;
}
const childActiveRoute = route.routes[route.index] as NavigationState;
return getActiveRouteState(childActiveRoute);
}
Usage:
const activeRoute = getActiveRouteState(this.props.navigation.state);
I'm using this when I need to get current active screen's state from NavigationDrawer.
This works fine in react-navigation v5.x
this.props.route.name
const routeNameRef = React.createRef();
<NavigationContainer
ref={navigationRef}
onReady={() => routeNameRef.current = navigationRef.current.getCurrentRoute().name}
onStateChange={() => {
const previousRouteName = routeNameRef.current
const currentRouteName = navigationRef.current.getCurrentRoute().name
if (previousRouteName !== currentRouteName) {
// Do something here with it
}
// Save the current route name for later comparision
routeNameRef.current = currentRouteName
}}
>
{/* ... */}
</NavigationContainer>
);
export function getCurrentRouteName(action) {
return routeNameRef;
}
You can import the function getCurrentRouteName and use this to get the current route name and its working in any nested navigators in React Navigation 5.
While using "react-navigation": "^3.0.8" and DrawerNavigator it can be accessed from the this.props object using
this.props.activeItemKey
Preparation
register šNavigationService.js,see the doc detail in Navigating without the navigation prop
<App
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
/>
recursion function
function getCurrentRoute(nav){
if(Array.isArray(nav.routes)&&nav.routes.length>0){
return getCurrentRoute(nav.routes[nav.index])
}else {
return nav.routeName
}
}
get current routeName
getCurrentRoute(NavigationService.getNavigator().state.nav)
In React Navigation v5, I was able to pull the current route name with the below approach:
import { useNavigationState } from '#react-navigation/native'
const routes = useNavigationState(state => state.routes)
const currentRoute = routes[routes.length -1].name
console.log('currentRoute: ',currentRoute)
It is possible to get this from the navigationRef attached to the navigation container. Where navigationRef is a ref.
export const navigationRef = React.createRef()
<NavigationContainer
ref={navigationRef}
>
<Navigator />
</NavigationContainer>
Then use: const currentRouteName = navigationRef.current.getCurrentRoute().name
Alternatively in a functional component you can useRef const navigationRef = React.useRef()
For react-navigation v5, you could use the useNavigationState hook -
import {useNavigationState} from '#react-navigation/native';
const state = useNavigationState(state => state);
const routeName = (state.routeNames[state.index]);
console.log(routeName);
import {getFocusedRouteNameFromRoute,useRoute} from '#react-navigation/native';
//...
const route = useRoute();
const routeName = getFocusedRouteNameFromRoute(route); // Get Nested Route Name
With version 5.x the best way currently is getFocusedRouteNameFromRoute
import { getFocusedRouteNameFromRoute } from '#react-navigation/native';
export default function Stack(route) {
// If the focused route is not found, we need to assume it's the initial screen
// This can happen during if there hasn't been any navigation inside the screen
// In our case, it's "Feed" as that's the first screen inside the navigator
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Feed';
return <> ..... </>
}
import { useNavigation } from '#react-navigation/native';
const App = () => {
const navigation = useNavigation();
const { dangerouslyGetState } = useNavigation();
const { index, routes } = dangerouslyGetState()
console.log(routes[index].name);
return(
<>
</>
)
};
You can use this in hooks as well.
console.log(navigation.dangerouslyGetState());
this.props.navigation.state.routeName works only in react-navigation 4 but react-navigation 5 doesn't support it.
The current route name can be achieved by using redux:
-Navigator component passes route object as a prop to the child component
-The Child component receives props and could find the route name in route.name
-To get updated route name on the screen change you can use focus event listener on navigation
<====== Parent Component where navigation is implemented ======>
import React from "react";
import { createMaterialTopTabNavigator } from "#react-navigation/material-top-
tabs";
import ChildScreen from "../screens/Home/childScreen";
const Tab = createMaterialTopTabNavigator();
const ParentNavigations = () => {
return (
<Tab.Navigator
>
<Tab.Screen name="ChildScreen" component={ChildScreen} />
</Tab.Navigator>
);
};
export default ParentNavigations;
<===== Child component =====>
import React, { useEffect } from "react";
import { View, StyleSheet } from "react-native";
import { useDispatch } from "react-redux";
import ActionTypes from "../../store/actions/ActionsTypes";
const ChildScreen = ({ navigation, route }) => {
const dispatch = useDispatch();
useEffect(() => {
const unsubscribe = navigation.addListener("focus", () => {
dispatch({ type: ActionTypes.SETROUTE, payload: route.name }); // every time when screen gets focued it will update the route through redux
});
return unsubscribe;
}, [navigation, route]);
return (
<View style={styles.container}>
<Text>Hello</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#0C0B0B",
},
});
export default ChildScreen;
If you just want to see if the current screen is focused, you can use navigation.isFocused().
https://reactnavigation.org/docs/navigation-prop/#isfocused
Example:
_backAction() {
const { navigation } = this.props;
if (navigation.isFocused()) {
this.setState({
isLeavingApp: true,
});
}
}
const Home = ({ navigation, route }) => {
// you will see screen key, name and params
console.log("ROUTE", route);
// rest of your code
};
For react native navigation 5.x use :
props.state.routeNames[props.state.index]
In one line with useNavigationState Hook:
const screenName = useNavigationState((state) => state.routes[state.index].name)
If you are using React Navigation v6 you can use this:
import { useRoute } from '#react-navigation/native';
...
const route = useRoute();
console.log('Current Route: ', route.name);
And if you want to get the name of the screen that you are, and you are inside a nested navigator, you can do this:
import { useNavigationState } from '#react-navigation/native';
...
const routes = useNavigationState(state => state.routes);
const currentRouteIndex =
routes?.length && routes[routes.length - 1].state?.index;
const currentRoute =
routes[routes.length - 1].state?.routeNames[currentRouteIndex];
console.log('Current Route: ', currentRoute);
This simple code worked for me. Just add this function to your Util.ts/js file and from your component pass the navigation as the object.
export const getCurrentScreenName = (navigation: any) => {
return navigation.getState().routes[navigation.getState().index].name;
};
This is step by step procedure of what Justin.Mathew has described in his answer.
Create a new file called RootNavigation.js and put the below content inside.
// RootNavigation.js
import * as React from 'react';
export const navigationRef = React.createRef(); // we will access all navigation props by importing this in any of the component
Now import the navigationRef from the RootNavigation.js file, and assign NavigationContainer ref to this. After this step navigationRef can function as navigation prop globally.
// App.js
import { NavigationContainer } from '#react-navigation/native';
import { navigationRef } from './RootNavigation';
export default function App() {
handleNavigationRef = (ref) => {
// DON'T DO navigationRef = ref, cause this will give you "navigationRef is
// read only" error.
navigationRef.current = ref;
}
return (
<NavigationContainer ref={handleNavigationRef}>
{/* ... */}
</NavigationContainer>
);
}
USAGE
Now you can import navigationRef in any of the file, even nested ones. And can use this to get the currentRoute and screen details.
//SomeNestedComonent.js
import { navigationRef } from "path/to/RootNavigation.js";
const route = navigationRef.current?.getCurrentRoute(); //current route object
const currentScreen = route.name; // current screen name
I have multiple TabNavigators nested in a BottomTabNavigator. I get the current route of the TabNavigator with:
const pathAndParams = props.navigation.router.getPathAndParamsForState(props.navigation.state) || {}
const activePath = pathAndParams.path
This worked for me (I did it inside my navigation drawer)!
const getCurrentRoute = nav => {
if (Array.isArray(nav.routes) && nav.routes.length > 0) {
return getCurrentRoute(nav.routes[nav.index]);
} else {
return nav.routeName;
}
};
const currentNavigation = getCurrentRoute(this.props.navigation.state);
If you are using reach navigation version 6 you can retrieve screen name by
props.route.name
We have a lot of answer here but it is hard to apply the fix because navigation is NULL.
WHY?
Scenario 1: We are using hooks function like: useRoute, useNavigationState,... but the navigation don't be mounted yet. So it is null and get the Error.
Scenario 2: We are using navigation object in the current screen like HomeScreen
const Home = ({ navigation, route }) => {
console.log("ROUTE", route);
// rest of your code
};
but navigation is NULL in Root app with presence of NavigationContainer
SOLUTION
Make sure to checking navigation is not NULL by using onReady() method of React navigation.
const navigationRef = useRef();
const [routeName, setRouteName] = useState('');
return (
<NavigationContainer
ref={navigationRef}
onReady={() => {
const currentRoute = navigationRef.current.getCurrentRoute();
setRouteName(currentRoute.name);
// Do whatever you want with navigation here!.
}}>
...
</NavigationContainer>);
That's it.
this worked for me try this..
const getCurrentRouteName = () => {
let _index = props.state.index;
let _routeName = props.state.routeNames;
return _routeName[_index]
}
For 'wix/react-native-navigation' below is my working solution,
import { Navigation } from 'react-native-navigation';
// Create a variable and set the value from navigation events
let navComponent = null
Navigation.events().registerComponentDidAppearListener(event => navComponent = event)
// navComponent will have the following structure
{"componentId": "Component9", "componentName": "HomeScreen", "componentType": "Component", "passProps": {}}
In my case, I needed to get the bottom nav index as well, this was my method
import {useNavigationState} from '#react-navigation/native';
then
const routes = useNavigationState(state => state.routes);
let place = routes[routes.length - 1];
if (place.name === 'YOUR_BOTTOM_NAV_NAME') {
if (place.state.index === 0) {
//you are in the main screen(BOTTOM_NAV : index 0)
} else {
//else navigate to index 0 screen
navigation.navigate('FirstScreen');
}
} else if (place.name === 'Another_Screen') {
navigation.navigate('navigate_to_the_place_you_want');
} else {
//otherwise come to the first screen
navigation.navigate('FirstScreen');
}
Try this,
const appNavigation = useNavigation();
const currentRoute = appNavigation.getCurrentRoute();
This worked for me. Navigation, and its state received as props were unreliable(at least for drawer navigator at root).
So I went with this one, which seems to be giving the global navigation state.
Had to use the navigation prop being received in drawer for drawer specific functions like closeDrawer or openDrawer.
export function AppDrawer(props) {
// for closeDrawer, openDrawer etc.
const { navigation: drawerNavigation } = props;
// for referencing current route
const appNavigation = useNavigation();
const currentRoute = appNavigation.getCurrentRoute();
// ... rest of the code
}
Reference for both the variable in console -
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();
I would like to have button change its color when pressed. I tryed checking out other similar topics but I couldn't find a solution. The code renders and the initial button color is red, but when I press it, nothing happens.
export default class someProgram extends Component {
render() {
var buttonColor = "red";
function changeButtonColor(){
if(this.buttonColor === "red"){
this.buttonColor = "green";
}
else{
this.buttonColor = "red";
}
}
return (
<View style={styles.container}>
<Button
title="Press me!"
color={buttonColor}
onPress={() => {changeButtonColor(buttonColor)}}
/>
</View>
);
}
}
You should keep track of the color in the component state. As a side, make sure to understand what the keyword this really means. Do a console.log(this) and see it for yourself.
Anyway, you can
(1) set the initial state in the constructor;
(2) access value from the state using this.state.someProp
then (3) adjust the state later using this.setState({ someProp: someValue }).
1) Initial State
constructor(props) {
super(props);
this.state = {
buttonColor: 'red'; // default button color goes here
};
}
2) Accessing the State &
3) Setting New State
onButtonPress = () => {
this.setState({ buttonColor: 'someNewColor' });
}
render() {
// ...
return (
{/* ... */}
<Button
color={this.state.buttonColor}
onPress={onButtonPress}
/>
)
Note some parts of the code were omitted to focus on the question at hand.