Cannot read property 'navigate' of undefined in react-navigation - react-native

I have used import { StackNavigator } from 'react-navigation'; in my Router.js
import { StackNavigator } from 'react-navigation';
import LoginForm from './components/LoginForm';
import EmployeeList from './components/EmployeeList';
import EmployeeCreate from './components/EmployeeCreate';
const RouterComponent = StackNavigator(
{
LoginForm: {
screen: LoginForm,
navigationOptions: {
title: 'Please Login'
}
},
EmployeeList: {
screen: EmployeeList,
},
EmployeeCreate: {
screen: EmployeeCreate,
navigationOptions: {
title: 'Create Employee'
}
}
},
{
initialRouteName: 'LoginForm',
}
);
export default RouterComponent;
Of course i use it in my App.js
import React from 'react';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';
import reducers from './src/reducers';
import Router from './src/Router';
export default class App extends React.Component {
render() {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
return (
<Provider store={store}>
<Router />
</Provider>
);
}
}
And i can use this.props.navigation in my LoginForm.js like this function:
onButtonPress() {
const { email, password, navigation } = this.props;
this.props.loginUser({ email, password, navigation });
}
I pass navigation to my Action file , i can use it to navigate another screen , like this:
const loginUserSuccess = (dispatch, user, navigation) => {
dispatch({
type: LOGIN_USER_SUCCESS,
payload: user
});
//navigation is from LoginForm.js , navigate to EmployeeList is working
navigation.navigate('EmployeeList');
};
Now i try to use this.props.navigation.navigate in my ListItem.js
My ListItem is under EmployeeList.js
Here is my EmployeeList.js
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { View, Text, Button, FlatList } from 'react-native';
import { employeesFetch } from '../actions';
import ListItem from './ListItem';
class EmployeeList extends Component {
static navigationOptions = ({ navigation }) => ({
title: 'EmployeeList',
headerLeft: null,
headerRight: <Button title="Add" onPress={() => navigation.navigate('EmployeeCreate')} />,
});
componentWillMount() {
this.props.employeesFetch();
}
// Using ListItem over here
renderRow(employee) {
return <ListItem employee={employee} />;
}
render() {
console.log(this.props);
return (
<FlatList
data={this.props.employees}
renderItem={this.renderRow}
keyExtractor={employee => employee.uid}
/>
);
}
}
const mapStateToProps = state => {
const employees = _.map(state.employees, (val, uid) => {
return { ...val, uid };
});
return { employees };
};
export default connect(mapStateToProps, { employeesFetch })(EmployeeList);
Here is my problem use this.props.navigation.navigate in ListItem.js
import React, { Component } from 'react';
import { Text, View, TouchableWithoutFeedback } from 'react-native';
import { CardSection } from './common';
class ListItem extends Component {
onRowPress() {
this.props.navigation.navigate('EmployeeCreate');
}
render() {
const { item } = this.props.employee;
return (
<TouchableWithoutFeedback onPress={this.onRowPress.bind(this)}>
<View>
<CardSection>
<Text style={styles.titleSytle}>
{item.name}
</Text>
</CardSection>
</View>
</TouchableWithoutFeedback>
);
}
}
const styles = {
titleSytle: {
fontSize: 18,
paddingLeft: 15
}
};
export default ListItem;
I can use this.props.navigation in my LoginForm.js , i can't figure it out why i use it in ListItem.js navigate is undefined ?
Any help would be appreciated. Thanks in advance.

in file EmployeeList.js pass navigation as prop to ListItem.
renderRow(employee) {
return <ListItem employee={employee} navigation={this.props.navigation} />;
}
Now you should be able to access navigation using this.props.navigation inside ListItem.js.
Just an observation, never bind methods to context inside the render
function as it is called repeatedly and a new instance will be created
each time. Change your ListItem.js as below.
class ListItem extends Component {
constructor(props) {
super(props);
this.onRowPress = this.onRowPress.bind(this); // here we bind it
}
onRowPress() {
this.props.navigation && this.props.navigation.navigate('EmployeeCreate');
}
render() {
const { item } = this.props.employee;
return (
<TouchableWithoutFeedback onPress={this.onRowPress}>
<View>
<CardSection>
<Text style={styles.titleSytle}>
{item.name}
</Text>
</CardSection>
</View>
</TouchableWithoutFeedback>
);
}
}

Use withNavigation. In your ListItem.js file add import { withNavigation } from ‘react-navigation’; and replace export default ListItem; with export default withNavigation(ListItem);

Here is what I did (using latest react native with ES6):
Refactored the class code from this:
export default class MyComponent extends React.Component {
// content & code
}
To look like this:
import { withNavigation } from 'react-navigation';
class MyComponent extends React.Component {
// content & code
}
export default withNavigation(MyComponent);
According to the docs (withNavigation):
"withNavigation is a higher order component which passes the navigation prop into a wrapped component."

Related

this.props.navigation.toggedrawer is not a function-React Native 0.59.9 React Navigation 3.11.0

I have a requirement of using Redux in Tabnavigator which is inside of
an DrawerNavigator.Redux Portion is working fine, But I am unable to
open the drawer on click of a button but the drawer is visible on
swipe gesture.
I am providing my code:-
App.js:-
import * as React from 'react';
import {createAppContainer,createStackNavigator,createBottomTabNavigator} from 'react-navigation';
import RegisterStack from './routes/Register.route';
import SplashStack from './routes/Splash.route';
import Test from './src/pages/Test';
const AppNavigator = createStackNavigator(
{
Splash: SplashStack,
Register: RegisterStack,
StartTest :Test
},
{
initialRouteName: 'StartTest',
headerMode:'none'
});
let Navigation = createAppContainer(AppNavigator);
export default class App extends React.Component {
render() {
return (
<Navigation />
);
}
}
code in Test.js:-
import React from 'react';
import { View,StyleSheet,Text,SafeAreaView,Dimensions,Button } from 'react-native';
import {createAppContainer,createMaterialTopTabNavigator,createDrawerNavigator, DrawerActions} from 'react-navigation';
import ReferenceStack from './Reference';
import QuestionStack from './Question';
import { Provider, connect } from 'react-redux';
import { createStore, combineReducers } from 'redux';
function counter(state, action) {
if (typeof state === 'undefined') {
return 0;
}
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
let store = createStore(combineReducers({ count: counter }));
class DrawerLayout extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<SafeAreaView style={styles.container}>
<Text>{this.props.count}</Text>
<Button
title="Increment"
onPress={() => this.props.dispatch({ type: 'INCREMENT' })}
/>
</SafeAreaView>
)
}
}
const styles = StyleSheet.create({
container: {
flex:1,
backgroundColor: "red",
}
});
let CounterContainer = connect(state => ({ count: state.count }))(ReferenceStack);
let StaticCounterContainer = connect(state => ({ count: state.count }))(QuestionStack);
let StaticCounterContainerDrawer = connect(state => ({ count: state.count }))(DrawerLayout);
const DrawerStack = createDrawerNavigator({
Test: {
screen: createMaterialTopTabNavigator({
Reference: {
screen: CounterContainer
},
Question:{
screen: StaticCounterContainer
}
},{
tabBarPosition: 'bottom',
tabBarOptions:{
activeTintColor:'#d61822',
inactiveTintColor:'#5e5e5e',
pressColor:'#d61822'
}
})
}
},{
drawerPosition: "left",
drawerWidth: Dimensions.get('screen').width*.80,
contentComponent: StaticCounterContainerDrawer
});
let DrawerTabNavigation = createAppContainer(DrawerStack);
class Test extends React.Component {
constructor(props) {
super(props);
console.log(props)
}
render() {
return (
<View style={{flex:1}}>
<View style={{height:50,backgroundColor:'yellow'}}></View>
<Provider store={store}>
<DrawerTabNavigation/>
</Provider>
<View style={{height:50,backgroundColor:'yellow'}}>
<Button
title="Open Drawer"
onPress={() => this.props.navigation.toggleDrawer()}
/>
</View>
</View>
);
}
}
export default Test;
When ever Open Drawer button is pressed
_this3.props.navigation.toggedrawer is not a function this error is showing.
I am providing the screen and the error also:-
Please hekp me to open the drawer on click of a button. Thanks in
advance
this.props.navigation.toggedrawer will work only for the screens which are in DrawerNavigator.
May be try this.
import { DrawerActions } from "react-navigation";
this.props.navigation.dispatch(DrawerActions.toggleDrawer())

how to connect react with redux?

so I am trying to learn about react-redux using react-native and I want to make a page where I can input a number and press login. when I press login, the page will alert me the number I input and saved into the store I created with redux.
can anyone please tell me what i'm doing wrong and what should I add or do to make it work?
below is my testing page
import React, {Component} from 'react';
import {View, TextInput, TouchableOpacity, Text} from 'react-native';
import {connect} from 'react-redux';
import actions from '../Redux/Action';
class tes extends Component{
constructor(props){
super(props)
}
render(){
return(
<View>
<TextInput placeholder="phone number"
keyboardType="number-pad"/>
<TouchableOpacity onPress={this.props.onLogin}>
<Text>login</Text>
</TouchableOpacity>
</View>
)
}
}
mapStateToProps = state => {
return {
number: state.phoneNumber
}
}
mapDispatchToProps = dispatch => {
return {
onLogin: (number) => {
dispatch(actions.setLoginNumber(number))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(tes);
this is my store class
import {createStore} from 'redux';
import reducer from './Reducer';
export default createStore(reducer)
here is my reducer class
const reducer = (state = {
phoneNumber: '',
},action) => {
switch(action.type) {
case "LOGIN":
state = {
phoneNumber: action.payload
}
break;
}
return state;
}
export default reducer;
{/* and this one my action class */}
export default function setLoginNumber(number) {
return{
type: "LOGIN",
payload: number
};
}
thanks in advance..
I think your not passing parameter number to onLogin function and you will need local state variable to hold the value. The code should be like this
import React, {Component} from 'react';
import {View, TextInput, TouchableOpacity, Text} from 'react-native';
import {connect} from 'react-redux';
import actions from '../Redux/Action';
class tes extends Component{
constructor(props){
super(props)
this.state = {
number: 0,
};
}
render(){
return(
<View>
<TextInput placeholder="phone number"
onChangeText={inputNumber => {
this.setState({ number: inputNumber })
}}
keyboardType="number-pad"/>
<TouchableOpacity onPress={() => {this.props.onLogin(this.state.number) }}>
<Text>login</Text>
</TouchableOpacity>
</View>
)
}
mapStateToProps = state => {
return {
number: state.phoneNumber
}
}
mapDispatchToProps = dispatch => {
return {
onLogin: (number) => {
dispatch(actions.setLoginNumber(number))
}
}
}
Answer for your second question -
You haven't passed created store to provider component of react-redux like below example
import { Provider } from 'react-redux';
import App from './App';
import store from './store';
export default class Root extends Component {
constructor() {
super();
}
render() {
return (
<Provider store={store}>
<App />
</Provider>
);
}
}
Hope it helps.

Changing tab bar background color causes all screens to re-render

I am using react-navigation as my navigation solution. I need to change the background color of my tab bar on click of a button in the settings screen, however, do so would make all screens re-render, here's a live demo of what's going on:
As you can see, every time I press the Change Tab Bar Background Color button in Settings screen, the color of the tab bar changes, however, at the same time, the app automatically navigates to Login screen for some reason. I am using redux to maintain the current theme, here are the code:
Action creator:
export function switchTheme() {
return { type: 'SWITCH_THEME' };
}
Reducer:
const INITIAL_STATE = {
backgroundColor: 'white'
};
export default function (state = INITIAL_STATE, action) {
switch (action.type) {
case 'SWITCH_THEME':
return { backgroundColor: state.backgroundColor === 'white' ? 'black' : 'white' };
default:
return state;
}
}
Settings screen:
import React, { Component } from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
import { Icon } from 'react-native-elements';
import { connect } from 'react-redux';
import { switchTheme } from '../actions';
class SettingsScreen extends Component {
static navigationOptions = {
title: 'Settings',
header: null,
tabBarIcon: ({ tintColor }) => {
return (<Icon name='settings' size={30} color={tintColor} />);
}
};
render() {
return (
<View style={{ ... }}>
<TouchableOpacity
onPress={this.props.switchTheme}
style={{ ... }}
>
<Text style={{ ... }}>
Change Tab Bar Background Color
</Text>
</TouchableOpacity>
</View>
);
}
}
export default connect(null, { switchTheme })(SettingsScreen);
Main.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { TabNavigator } from 'react-navigation';
import WelcomeScreen from './screens/WelcomeScreen';
import LoginScreen from './screens/LoginScreen';
import RegisterScreen from './screens/RegisterScreen';
import SettingsScreen from './screens/SettingsScreen';
import MessageScreen from './screens/MessageScreen';
class Main extends Component {
render() {
const MainTabNavigator = TabNavigator({
login: { screen: LoginScreen },
register: { screen: RegisterScreen },
message: { screen: MessageScreen },
setting: { screen: SettingsScreen }
}, {
tabBarOptions: {
style: { backgroundColor: this.props.backgroundColor }
}
});
return (
<MainTabNavigator />
);
}
}
const mapStateToProps = state => {
const { backgroundColor } = state.theme;
return { backgroundColor };
};
export default connect(mapStateToProps, null)(Main);
App.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import store from './store';
import Main from './Main';
class App extends Component {
render() {
return (
<Provider store={store}>
<Main />
</Provider>
);
}
}
export default App;
Why does the app navigates to Login screen and what can I do to prevent this from happening?
the backgroundcolor piece of state is changed in the reducer SWITCH_THEME
so I think when it comes to the main.js and pull out the backgroundColor from state.theme the prop backgroundColor gets updated and it re-renders

react-native-navigation cant navigate to another page

I searched and compare it to proper solutions nothing look wrong but I almost get the error screen shown below;
Whats wrong with navigation code here;
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableOpacity
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import About from './app/components/About';
export default class Home extends Component {
static navigationOptions = {
title: 'Welcome',
};
navigateToAbout(){
const { navigate } = this.props.navigation;
navigate('About')
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={()=>this.navigateToAbout()}>
<Text>Go to About Page</Text>
</TouchableOpacity>
</View>
);
}
}
const SimpleApp = StackNavigator({
Home: { screen: Home },
About: { screen: About },
});
AppRegistry.registerComponent('SimpleApp', () => SimpleApp);
I think your code is fine unless you have to bind the method (usually I use an arrow function, but it's ok if you want to bind the method in you constructor), maybe you can try like this way:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableOpacity
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import About from './app/components/About';
export default class Home extends Component {
static navigationOptions = {
title: 'Welcome',
}
navigateToAbout = () => {
this.props.navigation.navigate('About');
}
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<TouchableOpacity onPress={ this._navigateToAbout }
<Text>Go to About Page</Text>
</TouchableOpacity>
</View>
);
}
}
const SimpleApp = StackNavigator({
Home: { screen: Home },
About: { screen: About },
});
AppRegistry.registerComponent('SimpleApp', () => SimpleApp);
I hope it can help you :)
You didn't connect your home component with navigation, you should pass navigation method to the mapDispatchToProps object of connect method.
Here is an example:
import { connect } from 'react-redux'
import { NavigationActions } from 'react-navigation'
const navigate = NavigationActions.navigate
export class Home extends Component {
static navigationOptions = ({ navigation }) => ({
title: 'Welcome',
})
static propTypes = {
navigate: T.func.isRequired,
}
navigateToAbout(){
const { navigate } = this.props.navigation;
navigate({ routeName: 'About' })
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={()=>this.navigateToAbout()}>
<Text>Go to About Page</Text>
</TouchableOpacity>
</View>
);
}
}
export default connect(null, { navigate })(Home)

Cannot read property 'navigate' of Undefined in React Navigation

i'm React Native newbie. What i'm trying to do is added react navigation to my login page where user can click a button and navigate to the sign up page but i'm getting an error Cannot read property 'navigate' of Undefined. I've already searched the solution over an internet but no luck. This So does not help me - React Navigation - cannot read property 'navigate' of undefined and same with others .
Here is my code
index.ios.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import {StackNavigator} from 'react-navigation';
import Login from './src/screens/Login';
import Signup from './src/screens/Signup';
export default class tapak extends Component {
constructor(props) {
super(props);
this.buttonPress = this.buttonPress.bind(this);
}
render() {
return (
<View style={styles.container}>
<Text style={{color: 'blue'}} onPress={this.buttonPress}>sign up</Text>
</View>
);
}
buttonPress() {
console.log('called');
this.props.navigation.navigate('Signup');
}
}
const Stacks = StackNavigator({
Login: {
screen: Login
},
Signup:{
screen: Signup
}
});
Render the StackNavigator in your index.ios.js and move the button to the Login component:
const Stacks = StackNavigator({
Login: {
screen: Login
},
Signup:{
screen: Signup
}
});
class tapak extends Component {
render() {
return (
<Stacks />
);
}
}
Login.js :
export default class Login extends Component {
constructor(props) {
super(props);
this.buttonPress = this.buttonPress.bind(this);
}
buttonPress() {
console.log('called');
this.props.navigation.navigate('Signup');
}
render() {
return (
<View style={styles.container}>
<Text style={{color: 'blue'}} onPress={this.buttonPress}>sign up</Text>
</View>
);
}
}
Working example
here.
Write this code to index.ios.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import {StackNavigator} from 'react-navigation';
import Login from './src/screens/Login';
import Signup from './src/screens/Signup';
const Stacks = StackNavigator({
Login: {
screen: Login
},
Signup:{
screen: Signup
}
});
Login.js
import React ,{Component} from 'react';
import {
Text, View , Button,Image,
} from 'react-native';
export default class HomeScreen extends Component {
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Text
onPress={() => navigate('Signup')}
> SignUp</Text>
</View>
);
}
}
Hope this help you.
I think you need to include navigationOptions, for example:
class MyComponent extends React.Component {
static navigationOptions = {
title: 'Great',
// other configurations
}
render() {
return (
// your view
)
}
}
Also yu need to make sure you use AppRegistry.registerComponent('glfm', () => Stacks); rather than AppRegistry.registerComponent('glfm', () => tapak);
The only answer to this question is to just put const { navigate } = this.props.navigation in your render() function and then you can use it in any component that you need
For Example
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Text>This is the home screen of the app</Text>
<Button
onPress={() => navigate('Profile', { name: 'Brent' })}
title="Go to Brent's profile"
/>
</View>
);
}
Please read this doc for https://reactnavigation.org/docs/en/navigation-prop.html