undefined is not an object(evaluating 'this.props.navigation.navigate') - reactjs

Can someone help me? I keep getting the error (See title).
I know there are a lot of people asking this question before in StackOverflow. I read their answers but when I paste the solution, I still keep hiting this error.
Below are the code.
Thank you very much.
//Jimmy code
import { Navigation } from 'react-native-navigation';
import { StackNavigator } from 'react-navigation';
import { Merchant2 } from "./../Merchant2/Merchant2";
export class Login extends React.Component{
constructor(prop) {
super(prop);
Navigation.events().bindComponent(this);
this.state = {
userName: "",
password: "",
isVerify: false
};
this.onPress=this.onPress.bind(this);
}
onPress = () => {
//const { navigate } = this.props.navigation;
navigate('Page2');
alert(1)
}
render() {
navigationOptions = {
title: 'Results',
};
return (
<View style={[styles.formFooter]}>
<TouchableHighlight style={[commonStyles.alignItemsCenter, styles.loginBtn]}
activeOpacity={1}
underlayColor={'#cdcdcf'}
onPress={this.onPress.bind(this)}
>
);
};
}
const App = StackNavigator({
Home: { screen: Merchant2 },
});
export default App;

A few things here:
You should use methods with names different than the native ones such as 'onPress'
If you don't have a navigation props, it's simply because you never references it in your stack navigation. All items who's view is specified in a navigation object should have a navigation props.
navigationOptions should be tagged static if you define it in your component or like Login.navigationOptions. Also, if you want to add navigation options from the action of elements you create in your navigation options, you can do this like (in your component) : static navigationOptions = ({navigation}) => ({your navigation options here})

Related

How to access naviagtion options from imported file in react-native

I'm passing data through different pages down to the last page in my app, its been working fine.
But the issue is the last page has 2 components so the typical </ChatActivity navigation="{this.props.navigation}" />, here's what I mean:
I have an App.js
content of App.js
import ChatScreen from './chat'
class ChatActivity extends Component {
static navigationOptions = {
...
}
render() {
return(
<ChatScreen navigation={this.props.navigation} />
)
}
}
I also have chat.js that contains the chat component. Chat.js itself, needs to import Fire from './fire.js'
so now, this.props.navigation was only passed to Chat.js...but I need to access it from fire.js as well.
I've read about import {useNavigation}, but from what i have tried it didn't work cause my fire.js doesn't even look like the example in the docs
this is my fire.js
class Fire extends React.Component{
constructor (props) {
super(props)
this.init()
this.checkAuth()
}
init = () => {
firebase.initializeApp({
})
};
checkAuth = () => {
firebase.auth().onAuthStateChanged(user => {
if (!user) {
firebase.auth().signInAnonymously();
}
})
}
send = messages => {
messages.forEach(item => {
const message = {
text: item.text,
timestamp: firebase.database.ServerValue.TIMESTAMP,
// image: item.image,
//video: item.video,
user: item.user
}
this.db.child(`NEED NAVIGATION PARAMS HERE`).push(message)
})
}
parse = message => {
const {user, text, timestamp} = message.val();
const {key, _id} = message
const createdAt = new Date(timestamp)
return {
_id,
createdAt,
text,
user
}
}
get = callback => {
this.db.child(`NEED NAVIGATION PARAMS HERE`).on('child_added', snapshot => callback(this.parse(snapshot)))
}
off() {
this.db.off()
}
get db() {
return firebase.database().ref(`NEED NAVIGATION PARAMS HERE`);
}
get uid(){
return(firebase.auth().currentUser || {}).uid
}
}
export default new Fire();
Since i couldn't access navigation params, I tried AsyncStorage, but thats probably not the best practice and it isn't working too well. Not sure if its the AsyncStorage or react-native-gifted-chat but when I load the chat page once, it shows the same messages for other chats till I restart the app which shouldn't be cause i'm fetching the data based on unique parameters.
You have just missed one step here...
Since you have passed the navigation as props by using the following approach:
<ChatScreen navigation={this.props.navigation} />
the chat screen gets to use navigation properties of ChatActivity.
For Fire.js to be able to use the navigation as well, that was provided to Chat.js by ChatActivity you will need to pass the navigation props received by Chat.js to Fire.js in the same way.
This is how your Chat.js should look like:
import Fire from './Fire'
class Chat extends Component {
static navigationOptions = {
...
}
render() {
return(
<Fire navigation={this.props.navigation} />
)
}
}
That should solve the issue. Cheers!

How to create dynamic tabs in React Navigation v3

I have stack navigator wrapped with app container:
const AppNavigator = createStackNavigator({
Home: {
screen: Home,
},
});
export default createAppContainer(AppNavigator);
Home should have dynamic tabs. I want to load some info from backend and then generate tabs.
And here is my Home component:
class Home extends Component {
get tabs() {
return {
Main: { screen: Demo },
World: { screen: Demo },
};
}
get tabOptions() {
return {
// options...
};
}
render() {
const Tabs = createMaterialTopTabNavigator(this.tabs, this.tabOptions);
return <Tabs />;
}
}
In this case I am getting this error:
But if I am wrapping with createAppContainer...
const Tabs = createAppContainer(createMaterialTopTabNavigator(this.tabs, this.tabOptions));
...then I am getting warning about more than one container in app.
So how to make dynamic tabs in a right way?
UPD 1. Real code of getting tabs which I use now with yellow warning.
get tabs() {
const { categories } = this.props;
return reduce((acc, item) => assoc(prop('name', item), Demo, acc), {})(categories); // ramda
}
That error shows up because wrapping a navigator as a component is antipattern since the navigation prop is being broken that way.
instead, don't use a class , use plain functions, or you are gonna struggle so much like the other guys that tried to use a component
function tabs(){
return {
Main: { screen: Demo },
World: { screen: Demo },
}
}
function tabOptions(){
return {
// options...
};
}
export default createMaterialTopTabNavigator(tabs(), tabOptions())

Getting data from one class to make it the title of stackNavigator tab?

import { createStackNavigator } from "react-navigation";
import React, { Component } from "react";
class Navigator extends React.Component {
render() {
const TitleNameParam = navigation.getParam('TitleNameParam');
var TitleNameString = JSON.stringify(TitleNameParam);
var TitleNameObject = JSON.parse(TitleNameString);
}
}
const AppNavigator = createStackNavigator({
Launching: {
screen: LaunchingScreen,
navigationOptions: {
title: "Launching Soon",
headerTitleAllowFontScaling: true,
headerBackTitle: "Back"
}
},
InfoScreen: {
screen: InfoScreen,
navigationOptions: {
title: Navigator.TitleNameParam,
}
}
});
export default AppNavigator;
So what I want to do is get data (that's from an API) from one class to the stack navigator class so I can replace the title to whatever the data is. What I have right now is class Navigator getting data ( from TitleNameParam ) that makes the data into a string in which the navigator attempts to get it in the InfoScreen tab, but nothing shows up.
Use navigationOptions on the InfoScreen component:
class InfoScreen extends React.Component {
// Set the navigation options for `react-navigation`
static navigationOptions = ({navigation}) => {
return {
headerTitle: navigation.getParam('TitleNameParam');
}
}
…
}
When you navigate to this route use:
this.props.navigation.navigate('InfoScreen', {TitleNameParam: yourComputedTitleName})
Please let me know if this is correct.

How to pass props to 'screens'/components in react-navigation

I'm fairly new to programming in general and even newer to JS and React(Native) but I have worked on this for an entire day now and I still haven't figured it out so I have resorted to Stack Overflow in hopes that someone can help me.
Basically what I want to accomplish is to set other Components as children of the App component because I want them to be able to access information that I will set in the state of App. However, at the same time, I am also using react-navigation to create bottom navigation bars and thus I have no idea on how I can pass props of App to these other Components such as the ExplorePage component which is representative of the other children components.
App
import React from 'react';
import ExplorePage from './app/tabs/ExplorePage';
import {createBottomTabNavigator} from 'react-navigation';
...other imports
class App extends React.Component {
state = {
parentState: 'testing testing',
}
}
const MainScreenNavigator = createBottomTabNavigator(
{
Home: {screen: ExplorePage},
Search: {screen: SearchPage},
Favorites: {screen: FavoritesPage},
}
);
export default MainScreenNavigator;
ExplorePage, which is just like SearchPage and FavoritesPage
...imports
export default class ExplorePage extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
componentDidMount() {
console.log(this.props.parentState ? this.props.parentState : "Parent state does not exist what do :(");
}
render(){
return(
<Text>Testing</Text>
)
}
And obviously every time the console prints that parentState does not exist. I thought that being in the same place would give the other components like ExplorePage props of App. Thanks for helping me!
for those who are looking for a React Navigation 5 solution, you can use initialParams like this:
<Stack.Navigator>
<Stack.Screen
name="screenName"
component={screenComponent}
initialParams={{key: value}}
/>
</Stack.Navigator>
You could pass a props using function. Try this
import React from 'react';
import ExplorePage from './app/tabs/ExplorePage';
import {createBottomTabNavigator} from 'react-navigation';
...other imports
class App extends React.Component {
state = {
parentState: 'testing testing',
}
render() {
// old
// const MainScreenNavigator = mainScreenNavigator(this.state.parentState);
const MainScreenNavigator = mainScreenNavigator(this.state);
return (
<MainScreenNavigator />
)
}
}
const mainScreenNavigator = value => createBottomTabNavigator(
{
// Home: { screen : props => <ExplorePage {...props} parentState={value} /> },
Home: { screen : props => <ExplorePage {...props} {...value} /> },
Search: {screen: SearchPage},
Favorites: {screen: FavoritesPage},
}
);
export default App;
Edit
First thing, I changed your MainScreenNavigator to be a function, as it is accepting state values dynamically.
Second thing, Instead of directly assigning { screen : Component }, I used function. This is the feature provided by reactnavigation. You can find about this in the documentation. ReactNavigation
If you want to pass multiple attributes then you can use es6 spread operator, as shown in the edit. {...value}, this will pass all the property of value to that component.
You should use Navigator Props "screenProps" as mentionned in API:
screenProps - Pass down extra options to child screens
On child screen, just take props via this.props.screenProps

How to get current navigation state

I am using React Navigation's Tab Navigator from https://reactnavigation.org/docs/navigators/tab, when I switch between the Tab Screens I don't get any navigation state in this.props.navigation.
Tab Navigator:
import React, { Component } from 'react';
import { View, Text, Image} from 'react-native';
import DashboardTabScreen from 'FinanceBakerZ/src/components/dashboard/DashboardTabScreen';
import { TabNavigator } from 'react-navigation';
render() {
console.log(this.props.navigation);
return (
<View>
<DashboardTabNavigator />
</View>
);
}
const DashboardTabNavigator = TabNavigator({
TODAY: {
screen: DashboardTabScreen
},
THISWEEK: {
screen: DashboardTabScreen
}
});
DASHBOARD SCREEN:
import React, { Component } from 'react';
import { View, Text} from 'react-native';
export default class DashboardTabScreen extends Component {
constructor(props) {
super(props);
this.state = {};
console.log('props', props);
}
render() {
console.log('props', this.props);
return (
<View style={{flex: 1}}>
<Text>Checking!</Text>
</View>
);
}
}
I get props at Dashboard Screen when it renders the component first but I don't get props when I switch the tabs.
I need to get the current Screen name i.e TODAY or THISWEEK.
Your problem is about "Screen Tracking", react-navigation has an officially guide for this. you can use onNavigationStateChange to track the screen by using built-in navigation container or write a Redux middleware to track the screen if you want to integrate with Redux. More detail can be found at the officially guide: Screen-Tracking. Below is a sample code from the guide by using onNavigationStateChange:
import { GoogleAnalyticsTracker } from 'react-native-google-analytics-bridge';
const tracker = new GoogleAnalyticsTracker(GA_TRACKING_ID);
// gets the current screen from navigation state
function getCurrentRouteName(navigationState) {
if (!navigationState) {
return null;
}
const route = navigationState.routes[navigationState.index];
// dive into nested navigators
if (route.routes) {
return getCurrentRouteName(route);
}
return route.routeName;
}
const AppNavigator = StackNavigator(AppRouteConfigs);
export default () => (
<AppNavigator
onNavigationStateChange={(prevState, currentState) => {
const currentScreen = getCurrentRouteName(currentState);
const prevScreen = getCurrentRouteName(prevState);
if (prevScreen !== currentScreen) {
// the line below uses the Google Analytics tracker
// change the tracker here to use other Mobile analytics SDK.
tracker.trackScreenView(currentScreen);
}
}}
/>
);
Check all properties first, like
<Text>{JSON.stringify(this.props, null, 2)}</Text>
Above json array will show you current state of navigation under routeName index i.e.
this.props.navigation.state.routeName
Have you tried to define navigationOptions in your route object?
const DashboardTabNavigator = TabNavigator({
TODAY: {
screen: DashboardTabScreen
navigationOptions: {
title: 'TODAY',
},
},
})
You can also set navigationOptions to a callback that will be invoked with the navigation object.
const DashboardTabNavigator = TabNavigator({
TODAY: {
screen: DashboardTabScreen
navigationOptions: ({ navigation }) => ({
title: 'TODAY',
navigationState: navigation.state,
})
},
})
Read more about navigationOptions https://reactnavigation.org/docs/navigators/
Answer as of React Navigation v6
Depending on whether you want to trigger re-renders on value changes:
const state = navigation.getState();
or
const state = useNavigationState(state => state);
Reference:
https://reactnavigation.org/docs/use-navigation-state#how-is-usenavigationstate-different-from-navigationgetstate

Resources