this.props.navigation is not working in React Native - reactjs

I have the ff stacknavigation on my App.js:
<NavigationContainer>
<Stack.Navigator
initialRouteName={'BookListing'}>
<Stack.Screen name="Book List" component={Tabs} />
<Stack.Screen
name="BookDetailScreen"
component={BookDetail}
/>
</Stack.Navigator>
</NavigationContainer>
Now on my BookList Screen file, I created an onPressHandler function so I can navigate on the details screen and probably pass the data from redux state.
class BookListingScreen extends Component {
componentDidMount() {
this.props.fetchBooks();
}
onPressHandler = () => {
this.props.navigation.navigate('BookDetailScreen');
};
render() {
let content = (
<BookList
onPress={this.onPressHandler}
books={this.props.randomBooks.books}
/>
);
if (this.props.randomBooks.isFetching) {
content = <ActivityIndicator size="large" />;
}
return (
<View style={styles.container}>
<View>{renderHeader()}</View>
<View>{content}</View>
</View>
);
}
}
Here's my book list component:
export default class BookList extends Component {
_keyExtractor = item => item.cover_i;
render() {
return (
<FlatList
style={{flex: 1}}
data={this.props.books}
keyExtractor={this._keyExtractor}
renderItem={({item} = this.props.books) => {
return (
<BookItem onPressHandler={this.props.onPressHandler} item={item} />
);
}}
/>
);
}
}
Now on my item list, I am trying to use a TouchableOpacity to go the Details Screen.
export default class BookItem extends Component {
render() {
const {title, author_name, oclc = []} = this.props.item;
const onPressHandler = this.props.onPressHandler;
return (
<TouchableOpacity onPress={() => onPressHandler}>
<View style={styles.cardContainerStyle}>
<View style={{paddingRight: 5}}>
<Text style={styles.cardTextStyle}>{title}</Text>
</View>
</View>
</TouchableOpacity>
);
}
}
However, this doesn't go to the Details Screen. Any idea what's happening here?

change this in BookItem
onPress={() => onPressHandler}
to this
onPress={() => onPressHandler()}
or
onPress={onPressHandler}

Related

React-native navigation // move specific Drawer menu to title bar right button and hide the bottom menu which is linked with moved button

I'm using React-Navigation in React-native, and I'm a newbie to this, so I'm facing a number of difficult issues.
I am designing navigations with a mixture of Stack, Bottom and Drawer navigation.
My app has Intro Screen, Dashboard Screen, Profile Screen, Contact Screen.
Example Image
I thought almost successful, but I can't solve the problems below.
I want to move the "dashboard" menu(1) in Drawer to drawer title bar right button(2).
And I want to get rid of the duplicate "Dashoboard" menu(3) at the bottom.
my code..
// App.js
import React from 'react';
import Main from './components/Main';
class App extends React.Component {
render() {
return (
<Main />
);
}
}
// components/Main.js
import Intro from './__intro';
import DrawerNavigator from '../navigation/DrawerNavi';
const Stack = createStackNavigator();
class Main extends Component {
render() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name='Intro'
component={Intro}
options={
{headerShown: false}
}
/>
<Stack.Screen
name='Index'
component={DrawerNavigator}
options={
{headerShown: false}
}
/>
</Stack.Navigator>
</NavigationContainer>
)
}
}
export default Main;
// components/__intro.js
class Intro extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.container_child_1}>
<TouchableOpacity
activeOpacity={0.8}
style={styles.button}
onPress={
() => {
this.props.navigation.navigate('Index')
}
}
>
<Text style={styles.text}>Go to Dashboard</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
// navigation/DrawerNavi.js
import TabNavigator from "./TabNavigator";
import IndexTap from "../components/Index";
import DashBord from "../components/DashBoard";
const Drawer = createDrawerNavigator();
const DrawerNavigator = () => {
return (
<Drawer.Navigator>
<Drawer.Screen
name="dashboard from drawerNavi"
component={IndexTap}
/>
<Drawer.Screen name="Contact" component={Contact} />
</Drawer.Navigator>
);
}
export default DrawerNavigator;
// components/Index.js
import DashBoard from './DashBoard';
import Profile from './Profile';
const Tap = createBottomTabNavigator();
class IndexTap extends Component {
render() {
return (
<Tap.Navigator>
<Tap.Screen
name="DashBoard"
component={DashBoard}
options={{
headerShown: false,
),
}}
// initialParams={{ title: 'DashBoard' }}
/>
<Tap.Screen
name="Profile"
component={Profile}
options={{
headerShown: false
// headerTitleAlign: 'center'
}}
// initialParams={{ title: 'Profile' }}
/>
</Tap.Navigator>
)
}
}
// components/Contact.js
const Contact = () => {
return (
<View style={styles.center}>
<Text>This is the contact screen</Text>
</View>
);
};
// components/Dashboard.js
class DashBoard extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.container_child_1}>
<Text>DashBoard Page</Text>
<TouchableOpacity
activeOpacity={0.8}
style={styles.button}
onPress={
() => {
this.props.navigation.navigate('Intro')
}
}
>
<Text style={styles.text}>Go to the Intro</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
// components/Profile.js
class Profile extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.container_child_1}>
<TouchableOpacity
activeOpacity={0.8}
style={styles.button}
onPress={
() => {
this.props.navigation.navigate('DashBoard')
}
}
>
<Text style={styles.text}>DashBoard</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
how can I do?
Do I need to redesign?

I am trying to use react context api with react native class components but getting an error undefined provider.default.consumer

I am creating a component called provider to store my data
import React from "react";
export const MyContext = React.createContext();
export class MyProvider extends React.Component {
constructor(props) {
super(props);
this.state = {
isAuthenticated: false
};
}
render() {
return (
<MyContext.Provider
value={{
state: this.state,
changeAuthState: value => this.setState({ isAuthenticated: true })
}}
>
{this.props.children}
</MyContext.Provider>
);
}
}
I am trying to consume that data from a nested component called add photo so when the user is done with the account setup it will change the data to authenticated = true and show the main stack
//addPhoto.js
import Provider from "../../../provider";
export default class AddPhoto extends React.Component {
constructor(props) {
super(props);
this.state = {
image: null
};
}
render() {
return (
<Provider.Consumer>
{
(context = () => (
<View style={styles.conatiner}>
<View style={styles.authIocn}>
<SimpleLineIcons name="camera" size={wp("30%")} />
</View>
<View style={styles.header}>
<Text style={styles.headerText}>Add Profile Photo</Text>
</View>
<View style={styles.paragraph}>
<Text style={styles.paragraphText}>
Add a profile photo so your friends know its you.
</Text>
</View>
<Button block style={styles.button} onPress={this._pickImage}>
<Text style={styles.buttonText}>Add a Photo</Text>
</Button>
<View style={styles.skipButtonView}>
<TouchableOpacity>
<Text style={styles.skipButtonText}>Skip</Text>
</TouchableOpacity>
</View>
</View>
))
}
</Provider.Consumer>
);
}
}
getting error typeError: undefined is not an object evaluating ('_provider.default.Consumer')
I had to export the Provider class and context from provider.js
import React from "react";
const MyContext = React.createContext();
class Provider extends React.Component {
constructor(props) {
super(props);
this.state = {
isAuthenticated: "false"
};
}
render() {
return (
<MyContext.Provider
value={{
state: this.state,
changeAuthState: value => this.setState({ isAuthenticated: value })
}}
>
{this.props.children}
</MyContext.Provider>
);
}
}
export { Provider, MyContext };
Then, i needed to wrap my app.js in the provider
import { Provider } from "./provider";
return (
<Provider>
<NavigationContainer>
{signedIn ? (
<TabStack.Navigator
barStyle={{
backgroundColor: "#15212B",
borderColor: "#B0B8B3",
borderTopWidth: hp("0.05")
}}
>
<TabStack.Screen name={"Feed"} component={Feed} />
<TabStack.Screen name={"Discover"} component={Discover} />
<TabStack.Screen name={"Account"} component={Account} />
</TabStack.Navigator>
) : (
<AuthStack.Navigator headerMode={false}>
<AuthStack.Screen name={"SignUp"} component={SignUp} />
<AuthStack.Screen
name={"EmailOrPhone"}
component={EmailOrPhone}
/>
<AuthStack.Screen name={"SignIn"} component={SignIn} />
</AuthStack.Navigator>
)}
</NavigationContainer>
</Provider>
);
}
}
Then finally wrap my addPhoto.js file in the context
import { MyContext } from "../../../provider";
return (
<MyContext.Consumer>
{context => (
<View style={styles.conatiner}>
<View style={styles.authIocn}>
<SimpleLineIcons name="camera" size={wp("30%")} />
</View>
<View style={styles.header}>
<Text style={styles.headerText}>Add Profile Photo</Text>
</View>
<View style={styles.paragraph}>
<Text style={styles.paragraphText}>
Add a profile photo so your friends know its you.
</Text>
</View>
<Button block style={styles.button} onPress={this._pickImage}>
<Text style={styles.buttonText}>Add a Photo</Text>
</Button>
<View style={styles.skipButtonView}>
<TouchableOpacity>
<Text style={styles.skipButtonText}>Skip</Text>
</TouchableOpacity>
</View>
</View>
)}
</MyContext.Consumer>
);
}}

How to refresh main App class rendering in React-Native from another class

I'm trying to create my first App using React-Native,
i've created a class which renders the authentication form, after handling the submit the App should render the Navigation Screen with its tabs. I think i can "refresh" in someway the App class rendering from the Authentication Screen so it can check again if the user has authenticated or not, but i'm not really sure
App.Js:
import AuthScreen from './screens/AuthScreen';
export default class App extends React.Component {
state = {
isLoadingComplete: false,
isAuthenticated: false,
};
render() {
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
} else {
if(this.state.isAuthenticated == true) {
return (
<View style={styles.container}>
<StatusBar hidden = {true} />
<AppNavigator />
</View>
);
} else {
return (
<View style={styles.container}>
<StatusBar hidden = {true} />
<AuthScreen />
</View>
);
}
}
}
AuthScreen.js:
export default class AuthScreen extends Component {
handleSubmit = () => {
const value = this._form.getValue();
console.log('value: ', value);
}
render() {
return (
<View style={styles.container}>
<View style={styles.auth_container}>
<Form
ref={c => this._form = c}
type={User}
options={options}
/>
<Button
title="Submit"
onPress={this.handleSubmit}
/>
</View>
</View>
);
}
}
You can do this by using react-navigation(RN navigation library). But per code in question your trying to toggle between screen.
In your way: handleSubmit method of AuthScreen if success to following
handleSubmit = () => {
check auth logic
this.props.onSuccessFullLogin(value)
}
Update State in ParentComponent to toggle between screens App Component:
<AuthScreen /> this should be like <AuthScreen onSuccessFullLogin={()=>this.setState({isAuthenticated:true})} />

datepickerIOS not coming on the screen

I have a row where date is getting displayed on click of that view, I want a datepicker to pop up and display.
class EditProfilePicture extends React.Component {
state = {
currentDate: new Date()
};
setCurrentDateForIOS = currentDate => {
this.setState({ currentDate });
};
pickDate = () => (
<View>
{Platform.OS === "ios" ? (
<DatePickerForIOS
setCurrentDateForIOS={this.setCurrentDateForIOS}
/>
) : (
DatePickerForAndroid().then(currentDate =>
this.props.changeBirthDate({ currentDate })
)
)}
</View>
);
render() {
return (
<View>
<View style={styles.Container}>
<Text style={styles.heading}>Your birthday</Text>
<TouchableWithoutFeedback onPress={this.pickDate}>
<View style={styles.dropdown}>
<Text style={styles.textStyle}>
{DateFormatter(this.state.currentDate)}
</Text>
<Icon name="chevron-down" color={MED_GREY} />
</View>
</TouchableWithoutFeedback>
</View>
</View>
);
}
}
And in DatePickerForIOS I have this---
import React from "react";
import PropTypes from "prop-types";
import { DatePickerIOS } from "react-native";
const DatePickerForIOS = props => {
return (
<DatePickerIOS
date={new Date()}
mode="date"
onDateChange={() => props.setCurrentDateForIOS}
/>
);
};
DatePickerIOS.propTypes = {
setCurrentDateForIOS: PropTypes.func.isRequired
};
export default DatePickerForIOS;
While the code works fine for android, in IOS the screen contains NAN/NAN/NAN as text initially and the datepicker doesn't open up when clicked on the view.
DatePickerIOS needs to be part of the render function of EditProfilePicture
Currently you create the component fromonPress from TouchableWithoutFeedback.
You would need to do something like the docs say
render() {
return (
<View style={styles.container}>
<DatePickerIOS
date={this.state.chosenDate}
onDateChange={this.setDate}
/>
</View>
)
}
You would need to hide/show this a state boolean value to toggle on off.
Without knowing your Android code, I suspect that is acting like a popup modal.
Another solution would be to use a package like react-native-modal-datetime-picker which has the same interface between iOS and Android

React-native props.navigation.navigate undefined

I really can't get my navigation to work. My navigation works in DrawerNavigator menu but it does not work in components. I've tried everything but there must be something I still do not understand.
I always get an error: "TypeError: undefined is not an object (evaluating'_this.props.navigation')"
This is my View:
import React from 'react';
import { Provider } from 'react-redux';
import configureStore from '../../store/configure-store';
import StoresListContainer from '../../packages/stores/containers/list-container';
const store = configureStore({});
const Stores = () => {
return (
<Provider store={store}>
<StoresListContainer navigation={this.props.navigation} />
</Provider>
);
};
export default Stores;
This is my Component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
View,
Text,
ActivityIndicator,
FlatList,
Image,
TouchableNativeFeedback,
} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Styles from './styles';
import Theme from '../../../config/theme';
type Props = {
error: boolean,
loading: boolean,
data: Object,
fetchData: Function,
};
class ListComponent extends Component<Props> {
constructor(props) {
super(props);
this.goToPage = this.goToPage.bind(this);
}
componentDidMount() {
this.props.fetchData();
}
goToPage(param) {
this.props.navigation.navigate(param);
}
render() {
const hasData = Object.keys(this.props.data).length;
const errorMessage = () => {
return (
<Text>Ops! Ocorreu um erro ao carregar os dados.</Text>
);
};
const renderData = (item) => {
return (
<TouchableNativeFeedback contact={item} onPress={() => this.goToPage('Store')}>
<View style={Styles.renderData}>
<View style={{ flex: 0 }}>
<Image style={Styles.renderDataPicture} source={{ uri: item.item.image }} />
</View>
<View style={{ flex: 2 }}>
<Text style={Styles.renderDataTitle}>{ item.item.fantasy_name }</Text>
<Text style={Styles.renderDataSubtitle}>{ item.item.location }</Text>
</View>
<View style={Styles.renderDataItemIconContainer}>
<Icon name="chevron-right" style={Styles.renderDataItemIcon} />
</View>
</View>
</TouchableNativeFeedback>
);
};
const renderLoading = () => {
return (
<View style={Styles.renderLoading}>
<ActivityIndicator color={Theme.color.secondary} size="large" />
<Text style={Theme.text}>Aguarde</Text>
</View>
);
};
const getData = (data) => {
return (
<FlatList data={data} renderItem={renderData} keyExtractor={(item, index) => index} />
);
};
return (
<View style={Styles.container}>
{ this.props.loading ? renderLoading() : null }
{ this.props.error ? errorMessage() : null }
{ hasData ? getData(this.props.data) : null }
</View>
);
}
}
export default ListComponent;
You are using a functional stateless component instead of a class, therefore you need to drop the this.
const Stores = (props) => {
return (
<Provider store={store}>
<StoresListContainer navigation={props.navigation} />
</Provider>
);
};
// ES6 Class
class Stores extends Component {
render(){
return (
<Provider store={store}>
<StoresListContainer navigation={this.props.navigation} />
</Provider>
);
}
}

Resources