React Native: undefined is not an object (evaluating 'route.params') - react-native

undefined is not an object (evaluating 'route.params') is the error code, but I dont know how to fix it...
Screen1:
function Screen1({ navigation }) {
return (
between return and onPress are some components
onPress={() =>
navigation.navigate("Screen2", {
title: "Beispiel Titel",
author: "Beispiel Author",
genre: "Beispiel Genre",
})
Screen2:
function BookDetailScreen({ route, navigation }) {
const { title, author, genre } = route.params;
Can anyone help? Thanks! :)
I want to give the params from Screen 1 to Screen 2

Sometimes react-navigation hasn't populated the route object on the first render. For this reason, it's best to destructure it with null checks or defaults, i.e.
const { title, author, genre } = route?.params;
Or
const { params } = route || { title: 'default title', author: 'default author' }; // etc

Related

Programmatically update react navigation header title in functional component with hooks

question regarding react navigation setParams(). I asked on Reactiflux, but no one responded. I'm trying to set a title programmatically in a function component.
From another Stack Overflow thread, updating the static title retroactively, like this, works:
const Comp = props => { ... };
Comp.navigationOptions = ({ navigation }) => ({
title: 'Static Title'
});
But I need to access the component state from within the component, this does not work:
const Comp =({ navigation }) => {
const [title, setTitle] = useState('');
useEffect(() => {
navigation.setParams({ title });
}, [title]);
return ( ... );
}
If setParams() is the wrong way to do it, please enlighten me
EDIT: To add to this, when I console.log(navigation) I can see that it is changing navigation.state.params.title to the correct string, however it doesn't show up as the title.
You need to get the title param and apply it to the title:
Comp.navigationOptions = ({ navigation }) => ({
title: navigation.getParam('title', /* your default title */)
});
First you need to implement your navigation option as a function and this function needs navigation parameter as shown example in following lines :
const cameraScreenNavigator = {
'/auth/controlledCameraScreen': {
screen: ControlledCameraScreen,
navigationOptions: ({navigation}) => ({
title: navigation?.getParam('title', "Başvuru Adı"),
headerStyle: style.navigationHeaderBlue,
headerTitleStyle: {
fontWeight: 'bold',
color: COLORS.primaryText
},
}),
}
}
Then you can call setParams function in your component. This function acting as setState but react-nativagion suggests this for
setParams "setParams/setOptions etc. should only be called in useEffect/useLayoutEffect/componentDidMount/componentDidUpdate etc. Not during render or in constructor."
props.navigation.setParams({title: "New Title"})
resource : https://reactnavigation.org/docs/stack-navigator/

React navigation undefined params

I'm trying to pass params into a new screen, and implemented it like mentioned here.
I have the following TouchableOpacity button.
<TouchableOpacity
onPress={() => {
this.props.navigation.navigate('SomeScreen', {
title: 'Title',
subTitle: 'Subtitle',
});
}}
>
On the other page (let's call it Somescreen), I have the following:
render() {
const { navigation } = this.props;
const title = navigation.getParam('title');
}
But title above is undefined:
{ params: undefined, routeName: "Somescreen", key: "id-xx" }
My rootStack:
const RootStack = createStackNavigator({
SomescreenA: { screen: SomescreenA },
SomescreenB: { screen: SomescreenB },
}, { headerMode: 'none' });
Why are my params undefined in a new screen?
If you face a situation where your target screen get undefined params, probably you have a nested navigation stack.
Here you have to pass params to the navigate method in this way:
navigation.navigate('Root', {
screen: 'Settings',
params: { user: 'jane' },
});
For more information read this page in the official docs:
https://reactnavigation.org/docs/nesting-navigators/#navigating-to-a-screen-in-a-nested-navigator
In my specific case, I was calling a nested navigator, so I had to manage how send those params to their specific screen, so I did this:
Send params this way...the regular way:
navigation.navigate(
'OrderNavigator',
{itemSelected},
);
Then, from navigator stack I did this:
const OrderNavigator = ({route: {params}}) => {
return (
<Stack.Navigator initialRouteName="Order">
<Stack.Screen name="Order" component={Order} options={{headerShown: false}} initialParams={params} />
</Stack.Navigator>
);
};
And that's it. Then from the screen I got them like this:
const Order = ({route}) => {
const {itemSelected} = route.params;
const {first_name, last_name} = itemSelected;
return (...)
}
I've, unfortunately, encountered cases where navigate(route, params, ...) wouldn't pass the params object, just like you did.
As a workaround, I use the other variant - navigate({routeName, params, action, key}) that you can find here. It always works.
The accepted answer workaround did not work for me, so apparently if you use children to render your component (in screen options) and pass route as a prop, it works
if you are on react navigation v6^ use the useRoute hook to access the params object
const route = useRoute();
useRoute is a hook that gives access to the route object. It's useful when you cannot pass the route prop into the component directly, or don't want to pass it in case of a deeply nested child.
below is an implementation of this
import { useNavigation, useRoute } from '#react-navigation/native';
import { Pressable, Text } from 'react-native';
function Screen1() {
const navigation = useNavigation();
return (
<Pressable
onPress={() => {
navigation.navigate('Screen2', { caption: 'hey' });
}}
>
<Text> Go to Screen 2 </Text>
</Pressable>
);
}
function Screen2() {
const route = useRoute();
return <Text>{route.params.caption}</Text>;
}

Send props on navigation goBack

Basically I have three screens, the first is a stack navigator:
const stackNav = createStackNavigator({
Main: {
screen: MainScreen,
navigationOptions:({navigation}) => ({
header: null,
})
},
Detail: {
screen: DetailScreen,
navigationOptions: (props) => ({
title: "Detail",
})
}
})
The second one I have a button to go to the Detail screen:
<TouchableOpacity onPress={() => this.props.navigation.navigate("Detail", {name: l.name, subtitle: l.subtitle})}>
The last one is just information, I would like to click a button and execute:
this.props.navigation.goBack(), but sending props to the second screen (MainScreen), a setState and a integer id, how can I do that?
--EDIT WITH PARAMS--
I have this function in MainScreen:
handleOrdem(texto) {
console.log('texto: '+texto)
this.setState({
param: global.ordemAtiva,
ordemAtiva: !this.state.ordemAtiva
});
}
//The onPress code:
onPress={() => this.props.navigation.navigate("Detail", {name: l.name, subtitle: l.subtitle, ordemFunc: () => this.handleOrdem()})}>
and this is how I call it in Detail.screen:
execBack(param){
console.log('param: '+param);
this.props.navigation.state.params.ordemFunc(param);
this.props.navigation.goBack();
}
//Button to do it
onPress={() => this.execBack('test')}
Create a Method in parent screen
returnData(){
PERDROM_EVENT_WITH_RECEIVED_DATA
}
Then bind this method returnData with navigation code while executing navigation code
this.props.navigation.navigate("Detail", {name: l.name, subtitle: l.subtitle , returnData: this.returnData.bind(this)})}
In child Component call returnData method before call of goBack()
this.props.navigation.state.params.returnData(RETURN_DATA_YOU_WANT);
this.props.navigation.goBack();
Handling return data
Suppose you want two parameters back then add two parms in returnData() method
For example we took first param is boolean and second param String
returnData(flag,id){
USE THIS `flag` and `id` to update state or method call or
What ever you wanted too.
}
And inside Child component pass these two param
this.props.navigation.state.params.returnData(VALUE_OF `flag`, Value of `id`);
FOR EDIT WITH PARAMS
replace your code of navigation with this line
this.props.navigation.navigate("Detail", {name: l.name, subtitle: l.subtitle, ordemFunc: this.handleOrdem.bind(this)})>
You have to bind method not to call with arrow function
So the problem is
ordemFunc: () => this.handleOrdem()
Replace this line with
ordemFunc: this.handleOrdem.bind(this)
I came across this exact same issue and the problem is actually quite simple. We will utilise a callback for passing the params when we trigger goBack()
For example lets say I have two Views: ViewA and ViewB.
For which I will do as follows:
import useNavigation hook for setting up navigation.
When navigating to the next screen pass a callback ie: function with the specified paramaters for the values you would like to pass back to ViewA.
In ViewB use the route prop to get access to the params. In here you will find your callback.
Use your callback and pass in the correct arguments to your callback.
Call the navigation.goBack() to return to ViewA
In ViewA you will now have access to your value in your callback.
import React from "react";
import { View, Button } from "react-native";
import { useNavigation } from "#react-navigation/native";
const ViewA = () => {
const navigation = useNavigation();
return (
<View>
<Button
onPress={() =>
navigation.navigate("ViewB", {
handleItem: (item) => console.log(item), // will log out "Your Item"
})
}
/>
</View>
);
};
const ViewB = ({ route }) => {
const navigation = useNavigation;
return (
<View>
<Button
onPress={() => {
route.params.handleItem("Your Item");
navigation.goBack();
}}
/>
</View>
);
};
If you are using react-navigation v2 you no need to use navigation.goBack() to go back to Main screen
this.props.navigation.navigate('Main', { myParam: value }) will declaratively handle the navigation back (with same transition) for you
Maybe you can save the data in the global state and call it again when you goBack
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
console.log("its focus");
//call the new data and update state here
});
return unsubscribe;
}, [navigation]);

React-native-navigation Change state from another tabnavigator

I'm using react-navigation / TabNavigator, is there a way to change the state of a tab from another tab without using Redux or mobx?
Yes you can. It is a little complicated, a little hacky and probably has some side-effects but in theory you can do it. I have created a working example snack here.
In react-navigation you can set parameters for other screens using route's key.
When dispatching SetParams, the router will produce a new state that
has changed the params of a particular route, as identified by the key
params - object - required - New params to be merged into existing route params
key - string - required - Route key that should get the new params
Example
import { NavigationActions } from 'react-navigation'
const setParamsAction = NavigationActions.setParams({
params: { title: 'Hello' },
key: 'screen-123',
})
this.props.navigation.dispatch(setParamsAction)
For this to work you need to know key prop for the screen you want to pass parameter. Now this is the place we get messy. We can combine onNavigationStateChange and screenProps props to get the current stacks keys and then pass them as a property to the screen we are currently in.
Important Note: Because onNavigationStateChange is not fired when the app first launched this.state.keys will be an empty array. Because of that you need to do a initial navigate action.
Example
class App extends Component {
constructor(props) {
super(props);
this.state = {
keys: []
};
}
onNavigationChange = (prevState, currentState) => {
this.setState({
keys: currentState.routes
});
}
render() {
return(
<Navigation
onNavigationStateChange={this.onNavigationChange}
screenProps={{keys: this.state.keys}}
/>
);
}
}
And now we can use keys prop to get the key of the screen we need and then we can pass the required parameter.
class Tab1 extends Component {
onTextPress = () => {
if(this.props.screenProps.keys.length > 0) {
const Tab2Key = this.props.screenProps.keys.find((key) => (key.routeName === 'Tab2')).key;
const setParamsAction = NavigationActions.setParams({
params: { title: 'Some Value From Tab1' },
key: Tab2Key,
});
this.props.navigation.dispatch(setParamsAction);
}
}
render() {
const { params } = this.props.navigation.state;
return(
<View style={styles.container}>
<Text style={styles.paragraph} onPress={this.onTextPress}>{`I'm Tab1 Component`}</Text>
</View>
)
}
}
class Tab2 extends Component {
render() {
const { params } = this.props.navigation.state;
return(
<View style={styles.container}>
<Text style={styles.paragraph}>{`I'm Tab2 Component`}</Text>
<Text style={styles.paragraph}>{ params ? params.title : 'no-params-yet'}</Text>
</View>
)
}
}
Now that you can get new parameter from the navigation, you can use it as is in your screen or you can update your state in componentWillReceiveProps.
componentWillReceiveProps(nextProps) {
const { params } = nextProps.navigation.state;
if(this.props.navigation.state.params && params && this.props.navigation.state.params.title !== params.title) {
this.setState({ myStateTitle: params.title});
}
}
UPDATE
Now react-navigation supports listeners which you can use to detect focus or blur state of screen.
addListener - Subscribe to updates to navigation lifecycle
React Navigation emits events to screen components that subscribe to
them:
willBlur - the screen will be unfocused
willFocus - the screen will focus
didFocus - the screen focused (if there was a transition, the transition completed)
didBlur - the screen unfocused (if there was a transition, the transition completed)
Example from the docs
const didBlurSubscription = this.props.navigation.addListener(
'didBlur',
payload => {
console.debug('didBlur', payload);
}
);
// Remove the listener when you are done
didBlurSubscription.remove();
// Payload
{
action: { type: 'Navigation/COMPLETE_TRANSITION', key: 'StackRouterRoot' },
context: 'id-1518521010538-2:Navigation/COMPLETE_TRANSITION_Root',
lastState: undefined,
state: undefined,
type: 'didBlur',
};
If i understand what you want Its how i figure out to refresh prevous navigation screen. In my example I refresh images witch i took captured from camera:
Screen A
onPressCamera() {
const { navigate } = this.props.navigation;
navigate('CameraScreen', {
refreshImages: function (data) {
this.setState({images: this.state.images.concat(data)});
}.bind(this),
});
}
Screen B
takePicture() {
const {params = {}} = this.props.navigation.state;
this.camera.capture()
.then((data) => {
params.refreshImages([data]);
})
.catch(err => console.error(err));
}

sending the navigation params as props to other component

I am using RN v0.46.4 , react-navigation and sendbird.
I am developing a chat application.
What I am trying to do is that navigating the user to Msg screen with two params item (contain user info) and createdChannel .
They are passed but only once i.e. each time I navigate to Msg screen for different users , I receive the same value on which I have presses first.
Chat Screen
_chat(item){
// console.log(item);
const { navigate } = this.props.navigation
var userIds = [item.id,1];
sb = new SendBird({appId: APP_ID});
sb.connect(1, function(user, error) {
//console.log(user);
sb.GroupChannel.createChannelWithUserIds(userIds, true, item.firstname, function(createdChannel, error) {
if (error) {
console.error(error);
return;
}
//console.log(createdChannel);
navigate('Msg', { item, createdChannel })
});
Also, when I console createdChannel in _chat function , it gives the roght information as expected.
But when I console it in Msg screen , I receive only the first createdChannel created, already told above.
Msg Screen
super(props);
console.log(props.navigation.state.routes[1].params.createdChannel);
My router structure:
const CustomTabRouter = TabRouter(
{
Chat: {
screen: ChatStack,
path: ""
}
}
ChatStack
const ChatStack= StackNavigator({
Chat:{screen: Chats,
navigationOptions: {
header: null,
}},
Msg: {
screen: Msg,
navigationOptions: ({ navigation}) => ({
title: `${navigation.state.params.item.firstname} ${navigation.state.params.item.lastname}`,
tabBarVisible: false
})
},
})
In your Msg screen's constructor function, try accessing the item and createdChannel by calling props.navigation.state.params.createdChannel.
super(props);
console.log(props.navigation.state.params.createdChannel);
console.log(props.navigation.state.params.item);
I found myself in a similar situation. In my case the problem was because passing params to nested screen was intentionally removed from the lib (1.0.0-beta21 to 1.0.0-beta22).
Note: But as of now, react-navigation version is v1.5.2
https://github.com/react-navigation/react-navigation/issues/3252
Anyway, my quick and dirty solution is to use screenProps to inject props into screen.
const ChatStack= StackNavigator({
Chat:{ screen: Chats, ... },
Msg: { screen: Msg, ... },
});
// Instead of exporting ChatStack directly, create a wrapper and pass navigation.state.params to screenProps
export default (props) => {
const params = props.navigation.state.params;
// const params = _.get(props, 'navigation.state.params');
return (
<ChatStack screenProps={params} />
);
};
Now all screens under ChatStack can access those props injected.