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>
);
}
}
Related
I have this flatlist which lists all the jobs,upon click we are taken to a Job details page where you can apply to the job. On apply, the button name changes to "applied" and it is disabled. However when I apply to any job, all jobs are disabled. How do I make sure the context is unique to every job in the flatlist?
Home.js:
export default function Home(params) {
const{ navigation}=params;
...
return(
<FlatList
data={getCompanies(companies, filter)}
renderItem={({ item }) => {
return(
<TouchableOpacity onPress={()=>{ navigation.navigate('Job Details',item)}}>
<View style={styles.listingCard}>
<View style={styles.listingCardBody}>
<Text style={styles.listingCardTitle}>{item.title}</Text>
<Text style={styles.listingCardDescription}>
Here is a really simple description from {item.title} about the
role being advertized...
</Text>
</View>
</View>
</TouchableOpacity>
);
}
/>
);
}
Jobdetails:
import {clickableContext} from '../clickableContext';
import {applyButtonContext} from '../applyButtonContext';
function Jobdetails({route, navigation}){
const{ title, Category, rateHourly, responsibilities,location, postedBy,id,jobOverview, jobType, startDate, endDate, latitude, longitude}= route.params;
const { butText, setButText } = useContext(applyButtonContext);
const { clickable, setClickable } = useContext(clickableContext);
return(
<TouchableOpacity disabled={ clickable } style={page.button} onPress={()=>{
//apply() works fine
apply(id).then(async (result)=>{
if(result.succeeded()){
setButText('Application Successful!');
setClickable(true);
}
});
}}>
<Text style={page.text}>{ butText }</Text>
</TouchableOpacity>
);
}
app.js:
import 'react-native-gesture-handler';
import React, {useContext, useState} from 'react';
...
import { applyButtonContext } from './src/api/jobs/applyButtonContext';
import { clickableContext} from "./src/api/jobs/clickableContext";
export default function App() {
const [clickable, setClickable] = useState(false);
const [ butText, setButText] = useState('Apply Now');
return(
<>
{/* Providers */}
<clickableContext.Provider value={{ clickable, setClickable }}>
<applyButtonContext.Provider value={{butText, setButText}}>
<SafeAreaProvider>
<Provider store={store}>
<ThemeProvider>
<NavigationContainer theme={loremipsum}>
<StatusBar barStyle="dark-content" />
<AppNavigation />
</NavigationContainer>
</ThemeProvider>
</Provider>
</SafeAreaProvider>
</applyButtonContext.Provider>
</clickableContext.Provider>
</>
);
}
I am new to react native and have a problem figuring out how to navigate from one class to another one with passing parameters and would appreciate your help.
All I want to do is:
ClassA should have a checkbox with state handling and a flatlist containing CustomButton
Navigate from ClassA to TargetScreen by clicking CustomButton
Pass parameter "element" to TargetScreen
Show content of parameter passed in TargetScreen
The error message I get:
Error: Invalid hook call. Hooks can only be called inside of the body
of a function component. This could happen for one of the following
reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app See https://reactjs.org/warnings/invalid-hook-call-warning.html for tips about how to debug and
fix this problem.
ClassA:
import React, { Component, useState } from 'react';
import { useNavigation } from '#react-navigation/native';
import { CustomButton} from './CustomButton.js';
import { CheckBox, SafeAreaView, FlatList} from 'react-native';
class ClassA extends React.Component {
render() {
const [ASelected, setA] = useState(false);
const NavigateWithParams = () => {
navigation = useNavigation();
this.props.navigation.navigate('TargetScreen', { element: 'elementname' })
}
const renderItemCustom= ({ item }) => (
<CustomButton onPress={() => navigateWithParams()} />
);
}
return (
<CustomConst/>
<CheckBox value={ASelected}
onValueChange={{setA}} />
<SafeAreaView>
<FlatList
data={data}
renderItem={renderItemCustom}
keyExtractor={(item) => item.element}
/>
</SafeAreaView>
);
}
export default ClassA;
TargetScreen:
class TargetScreen extends React.Component {
render() {
const { navigation } = this.props;
return (
<Text> {JSON.stringify(navigation.getParam('element'))} </Text>
);
}
}
export default TargetScreen;
+++++++++++++++++++++++++
Update:
As of now the code looks like this:
class ClassA extends React.Component {
NavigateWithParams = (element) => {
this.props.navigation.navigate('TargetScreen', { element: 'elementname' })
}
renderItemCustom = ({ item }) => (
<CustomButton element={item.title} onPress={() => this.NavigateWithParams(item.element)} />
);
render() {
return (
<SafeAreaView>
<FlatList
data={data}
renderItem={this.renderItemCustom}
keyExtractor={(item) => item.id}
/>
</SafeAreaView>
);
}
}
export default ClassA;
And I am now getting this issue:
TypeError: Cannot read property 'navigate' of undefined
+++++++++++++++++++++++++
Update2
Routing:
function ClassA({ navigation }) {
return (
<ClassAScreen/>
);
function Target({ navigation }) {
return (
<TargetScreen/>
);
//navigation stacks
const SessionStack = createStackNavigator();
function SessionStackScreen({ navigation }) {
return (
<SessionStack.Navigator>
<SessionStack.Screen
name="ClassA"
component={ClassA}
options={{ tabBarLabel: 'ClassA!', headerShown: false }}
/>
<SessionStack.Screen
name="Target"
component={Target}
options={{ tabBarLabel: 'works!' }}
/>
</SessionStack.Navigator>
)
}
Logging gives me this:
renderItemCustom = ({ item }) => (
<CustomButton element={item.title} onPress={() => console.log(this.props)} />
);
+++++++++++++++++
Update:
Solution can be found here:
Navigation with parameters from custom element in Flatlist in React Native: Empty parameters
You cant use hooks inside a class component so remove the line which has the hook
and change like below
const NavigateWithParams = element => {
this.props.navigation.navigate('TargetScreen', { element: element })
}
const renderItemCustom= ({ item }) => (
<CustomButton onPress={() => this.navigateWithParams(item.element)} />
);
And parameter are passed using the route prop
class TargetScreen extends React.Component {
render() {
const { route} = this.props;
return (
<Text> {JSON.stringify(route.params.element)} </Text>
);
}
}
Also for the checkbox instead of using the useState hook, use this.state.
You can’t use Hooks inside a class component
https://reactjs.org/docs/hooks-faq.html#should-i-use-hooks-classes-or-a-mix-of-both
UPDATE:
Work variant, you can try here: https://snack.expo.io/#vasylnahuliak/stackoverflow-67862370
import 'react-native-gesture-handler';
import React from 'react';
import { Text, View, StyleSheet, Button, FlatList } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const DATA = [
{
id: 0,
title: 'first button',
element: 'something'
},
{
id: 1,
title: 'second button',
element: 'another something'
},
]
const HomeSceen = ({ route, navigation }) => {
return (
<View>
<Text>{JSON.stringify(route, null, 2)}</Text>
<Button
title="Navigate to ProfileScreen"
onPress={() => {
navigation.navigate('ProfileScreen');
}}
/>
</View>
);
};
const ProfileScreen = ({ route, navigation }) => {
const NavigateWithParams = (element) => {
navigation.navigate('HomeSceen', { element });
};
const renderItemCustom = ({ item }) => (
<Button title={item.title} onPress={() => NavigateWithParams(item.element)} />
);
return (
<View>
<Text>{JSON.stringify(route, null, 2)}</Text>
<FlatList
data={DATA}
renderItem={renderItemCustom}
keyExtractor={(item) => item.id}
/>
<Button
title="Navigate to HomeSceen"
color="tomato"
onPress={() => {
navigation.navigate('HomeSceen');
}}
/>
</View>
);
};
const SessionStack = createStackNavigator();
const App = () => {
return (
<NavigationContainer>
<SessionStack.Navigator>
<SessionStack.Screen name="HomeSceen" component={HomeSceen} />
<SessionStack.Screen name="ProfileScreen" component={ProfileScreen} />
</SessionStack.Navigator>
</NavigationContainer>
);
};
export default App;
I have developed a react native application. In there I am updating a state variable through redux-thunk action dispatch. Now what I want is I want to navigate to another component when It is updated. How I have written is like this.
useEffect(() => {
_navigateOnSuccessOtp(props.navigation);
}, [props.success, props.navigation]);
This is navigated to next view even when navigation is changed because It also has been added as a dependency to this effect. This is my full code of the component.
import React, {useEffect} from 'react';
import {
View,
TouchableOpacity,
KeyboardAvoidingView,
Switch,
TextInput,
BackHandler,
} from 'react-native';
import I18n from 'react-native-i18n';
import {Formik} from 'formik';
import * as Yup from 'yup';
import {connect} from 'react-redux';
import {signinActions} from '_store/actions';
import SubmitButton from '_components/submitButton/';
import AppText from '_components/appText';
import {strings} from '_translations/i18n';
import styles from './loginstyle';
import Icon from 'react-native-vector-icons/FontAwesome';
const keepMe = (e) => {
console.log(e);
};
const handleBackButton = () => {
return true;
};
const _onPress = (values, sendOTP) => {
console.log(values.mobileNo);
sendOTP(values.mobileNo);
// navigation.navigate('Otp');
};
const _navigateOnSuccessOtp = (navigation) => {
navigation.navigate('Otp');
};
const changeLanguage = (navigate) => {
navigate.navigate('Home');
};
const getCurrentLocale = () => {
let locale;
locale = I18n.locale;
if (locale === 'en') {
return <AppText styles={styles.bottomLinkText}>English</AppText>;
} else if (locale === 'ta') {
return <AppText styles={styles.bottomLinkText}>தமிழ்</AppText>;
}
};
const Login = (props) => {
useEffect(() => {
BackHandler.addEventListener('hardwareBackPress', handleBackButton);
return () => {
BackHandler.removeEventListener('hardwareBackPress', handleBackButton);
};
}, []);
useEffect(() => {
_navigateOnSuccessOtp(props.navigation);
}, [props.success, props.navigation]);
return (
<KeyboardAvoidingView style={{flex: 1}} enabled>
<View style={styles.signinView}>
<View style={styles.signinTitleView}>
<AppText styles={styles.signinTitle}>
{strings('login.title')}
</AppText>
</View>
<View style={styles.colempty} />
<View style={styles.signIn}>
<View style={styles.signInContainer}>
<AppText styles={styles.loginFormTitle}>
{strings('login.title')}
</AppText>
<View style={styles.formContainer}>
<Formik
initialValues={{
mobileNo: '',
toggle: true,
}}
validationSchema={Yup.object({
mobileNo: Yup.string().required('Mobile number required'),
})}
onSubmit={(values, formikActions) => {
_onPress(values, props.sendOTP);
setTimeout(() => {
formikActions.setSubmitting(false);
}, 500);
}}>
{(formprops) => (
<View>
<View
style={
(!formprops.touched.mobileNo
? styles.inputView
: null) ||
(formprops.values.mobileNo && !formprops.errors.mobileNo
? styles.validInputView
: null) ||
(formprops.touched.mobileNo && formprops.errors.mobileNo
? styles.inputViewError
: null)
}>
<TextInput
style={styles.textField}
placeholder={strings('login.mobile')}
placeholderTextColor="#bbbbbb"
value={formprops.values.mobileNo}
onChangeText={formprops.handleChange('mobileNo')}
onBlur={formprops.handleBlur('mobileNo')}
keyboardType="numeric"
/>
{formprops.touched.mobileNo &&
formprops.errors.mobileNo ? (
<Icon name="times" size={20} style={styles.errorIcon} />
) : null}
{formprops.touched.mobileNo &&
formprops.values.mobileNo ? (
<Icon name="check" size={20} style={styles.validIcon} />
) : null}
</View>
{formprops.touched.mobileNo && formprops.errors.mobileNo ? (
<View style={styles.errorMessage}>
<AppText styles={styles.errorMessageText}>
{formprops.errors.mobileNo}
</AppText>
</View>
) : null}
<View style={styles.togglebuttoncontainer}>
<View style={styles.toggleTextView}>
<AppText styles={styles.toggleText}>
{strings('login.keep-login')}
</AppText>
</View>
<View style={styles.toggleView}>
<Switch
trackColor={{false: '#dddddd', true: '#c1d6ee'}}
thumbColor={{false: '#ffffff', true: '#007aff'}}
ios_backgroundColor="#dddddd"
onValueChange={(value) =>
formprops.setFieldValue('toggle', value)
}
value={formprops.values.toggle}
style={styles.toggle}
/>
</View>
</View>
<SubmitButton
onpress={formprops.handleSubmit}
btext={strings('login.button-text')}
/>
</View>
)}
</Formik>
</View>
</View>
</View>
<View style={styles.hr} />
<View style={styles.signInBottomContainer}>
<View style={styles.signInBottomView}>
<View style={styles.signInBottomContainerTextView}>
<AppText styles={styles.signInBottomContainerText}>
{strings('login.language-change')}?
</AppText>
</View>
<View style={styles.signInBottomLinkView}>
<TouchableOpacity
onPress={() => changeLanguage(props.navigation)}>
{getCurrentLocale()}
</TouchableOpacity>
</View>
</View>
</View>
</View>
</KeyboardAvoidingView>
);
};
const mapStateToProps = (state) => {
console.log(state);
return {
success: state.signin.success,
};
};
const mapDispatchToProps = (dispatch) => {
return {
sendOTP: (number) => dispatch(signinActions.sendOtpActionCreator(number)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Login);
I tried a lot to figure out how to do it correctly but I was unable to do so. So can someone help me with this problem? Thank you.
Since , _navigateOnSuccessOtp only uses navigation.navigate you can simply pass props.navigation.navigate as a dependency for useEffect and also as an argument to the function. Now navigate does not change and hence your useEffect won't be triggered unnecessarily.
const _navigateOnSuccessOtp = (navigate) => {
navigate('Otp');
};
.....
useEffect(() => {
_navigateOnSuccessOtp(props.navigation.navigate);
}, [props.success, props.navigation.navigate]);
You could also remove props.navigation from the dependency as you don't want useEffect to trigger on props.navigation change. Please check this post for more details: How to fix missing dependency warning when using useEffect React Hook?
I have a component for dropdown (name : JDrop) :
import React, { Component } from 'react';
import { View,Picker,Image } from 'react-native';
export default class JDrop extends Component {
constructor(props,state) {
super(props)
this.state = {
selectedValue:'',
}
}
render() {
const {items,sizeW,bColor} = this.props;
return (
<View style={{flexDirection: 'row',height: 35, width: sizeW, alignItems: 'center',backgroundColor:bColor}}>
<View>
<Image style={{width:20,height:10, margin:5}} source={require('../../assets/Images/arrowb.png')}/>
</View>
<View >
<Picker
mode='dropdown'
style={{height: 35, width: sizeW,backgroundColor:bColor}}
selectedValue={this.state.selectedValue}
onValueChange={(itemValue, itemIndex) => this.setState({selectedValue: itemValue})}
prompt='Select...'
>
{
items.map( (item,ind) => (
<Picker.Item label={item.name} value={item.value} key={ind} />
))
}
</Picker>
</View>
</View>
)
}
}
I use it in App.js like :
<JDrop items={this.state.priority} sizeW={200} bColor={'blue'} ></JDrop>
how can I get selectedValue from JDrop in App.js ?
There are a few ways, but the easiest is to pass a change handler from the parent to the child that can communicate the changes in the child. Like this:
class App extends React.Component {
onChangeJDrop = (val) => {
console.log(val)
}
render() {
return (
<JDrop onChange={this.onChangeJDrop} items={this.state.priority} sizeW={200} bColor={'blue'} ></JDrop>
)
}
}
class JDrop extends React.Component {
onValueChange = (itemValue, itemIndex) => {
this.setState({selectedValue: itemValue})
this.props.onChange(itemValue)
}
render() {
return (
<Picker onValueChange={this.onValueChange} />
)
}
}
You will need to pass in a callback into your JDrop component (named onSelect below):
<JDrop onSelect={this.onSelect} items={this.state.priority} sizeW={200} bColor={'blue'} ></JDrop>
Then in your JDrop component:
onValueChange={(itemValue, itemIndex) => {
this.setState({selectedValue: itemValue})
this.props.onSelect(itemValue)
} }
I have created a component called OrderGuideSelect and I am trying to render it in another area of our app. The problem is the OrderGuideSelect component is not rendering. When I set up breakpoints I am able to hit inside of the renderOrderGuideOptions function but it never makes it into the OrderGuideSelect.js file. I also tried putting 'export default' in front of the class declaration instead of the connection but it didn't make a difference. Does anyone know how to get the OrderGuideSelect component rendering properly?
Here is where I call the function that renders the OrderGuideSelect component:
<TouchableOpacity onPress={() => this.renderOrderGuideOptions()}>
<MBIcon name="ico-24-filter" size={30} style={styles.filterIcon}/>
</TouchableOpacity>
And here is the rendering function:
renderOrderGuideOptions = () => {
return (
<View>
<OrderGuideSelect />
</View>
)
}
Here is the OrderGuideSelect.js file:
import React, {Component} from 'react';
import {View, FlatList, ActivityIndicator, StyleSheet} from 'react-native';
import {connect} from 'react-redux';
import {fetchOrderGuides} from '../../actions/AppActions';
import {orderGuideSelected} from '../../actions/ProductAction';
import Header from '../../components/Header/Header';
import {createIconSetFromIcoMoon} from 'react-native-vector-icons';
import selection from '../../selection';
import OrderGuideOption from './OrderGuideOption';
const MBIcon = createIconSetFromIcoMoon(selection);
class OrderGuideSelect extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.dispatch(fetchOrderGuides());
}
selectOrderGuide = id => {
this.props.dispatch(orderGuideSelected(id));
}
render() {
const {isLoading, orderGuides} = this.props.orderGuide;
return (
<View style={styles.wrapper}>
<Header />
<View style={styles.iconLine}>
<MBIcon name='ico-24-filter' style={styles.filterIcon} />
</View>
{isLoading &&
<ActivityIndicator
style={{alignSelf: 'center'}}
animating={true}
size='large'
/>
}
{!isLoading &&
<View style={styles.optionList}>
<FlatList
style={styles.optionList}
data={orderGuides}
keyExtractor={(item, index) => item.id.toString()}
renderItem={({item}) => <OrderGuideOption guideData={item} isSelected={item.id == this.props.selectedGuide.id} onSelected={this.selectOrderGuide} />}
/>
</View>
}
</View>
);
}
}
function mapStateToProps(state){
const {products, orderGuide} = state;
return {
selectedGuide: products.selectedOrderGuide,
orderGuide
}
}
export default connect(mapStateToProps)(OrderGuideSelect);
Also, I may be importing of the OrderGuideSelect component should be correct:
In your code calling this.renderOrderGuideOptions function on onPress event doesn't make sense, i.e. this.renderOrderGuideOptions returns the element but where to append it in DOM?
This should be achived using state in React. So you can set the state in onPress handler then use that state in render to show your OrderGuideOptions component.
So on onPress event bind the function handler:
<TouchableOpacity onPress={this.showOrderGuideOptions}>
<MBIcon name="ico-24-filter" size={30} style={styles.filterIcon}/>
</TouchableOpacity>
Now this showOrderGuideOptions will set the state named showOrderGuideFunction to true.
showOrderGuideOptions(){
this.setState({showOrderGuideFunction: true});
}
At last step use this showOrderGuideFunction state to render your component in the render function like this:
render() {
return (
<div>
...
{
this.state.showOrderGuideFunction &&
renderOrderGuideOptions()
}
</div>
)
}
You can do what you want probably holding a state property in your component and show your OrderGuideOptions according to this state property.
state = { showOrderGuideOptions: false };
renderOrderGuideOptions = () =>
this.setState( prevState => ( { showOrderGuideOptions: !prevState.showOrderGuideOptions }) );
render() {
return (
<View>
<TouchableOpacity onPress={this.renderOrderGuideOptions}>
<MBIcon name="ico-24-filter" size={30} style={styles.filterIcon}/>
</TouchableOpacity>
{ this.state.showOrderGuideOptions && <OrderGuideSelect /> }
</View>
)
}
I think you wanted to something similar to this
class RenderOrderGuideSelectComponent extends Component {
constructor(props) {
super(props);
this.state={
showOrderGuideSelect : false
};
}
renderOrderGuideOptions = () => {
this.setState({showOrderGuideSelect: true});
}
render() {
if(this.state.showOrderGuideSelect) {
return (
);
} else {
return (
this.renderOrderGuideOptions()}>
);
}
}
}