React Native Navigate to screen outside render - react-native

const Card = (props) => {
return (
<TouchableOpacity onPress={() => this.props.navigation.navigate('Route')}>
...
</TouchableOpacity>
);
};
export default class HorizontalList extends Component {
...
render() {
return (
<FlatList
...
renderItem={({item: rowData}) => {
return (
<Card
thumb_image={rowData.thumb_image}
title={rowData.title}
innerSubTitle={rowData.innerSubTitle}
innerMainTitle={rowData.innerMainTitle}
/>
);
}}
keyExtractor={(item, index) => String(index)}
/>
);
}
}
I want to add a onPress in Card that will open another screen in the app. Constant is declared outside the render and Card is called as a template inside the render function.

Pass navigation to Card
<Card
navigation={this.props.navigation}
thumb_image={rowData.thumb_image}
title={rowData.title}
innerSubTitle={rowData.innerSubTitle}
innerMainTitle={rowData.innerMainTitle}
/>
and use like this:
const Card = (props) => {
return (
<TouchableOpacity onPress={() => props.navigation.navigate('Route')}>
...
</TouchableOpacity>
);
};

Related

React Native call function outside of App. Flatlist access onPress function

There is example https://reactnative.dev/docs/flatlist
Let's say I want to add button in each flatlist item. All happens inside App.js
const Item = ({ item,.....}) => (
<TouchableOpacity onPress={onPress} style={..}>
<Button title='Go' onPress={() => myFunc('abc')} /> </TouchableOpacity>);
const App = () => {
function myFunc(x){
}
}
I get " ReferenceError: Can't find variable: myFunc "
I solved this by moving Item inside of const App = () => { but I think it might be wrong.
Tell me please, what is the correct way?
You could do something like this:
const App = () => {
const myFunc = (args) => {
// perform your action here.
}
return (
<FlatList
data={[{ title: 'Title Text', key: 'item1' }]}
renderItem={({ item, index, separators }) => (
<TouchableOpacity
key={item.key}
onPress={() => myFunc('abc')}
>
<View style={{ backgroundColor: 'white' }}>
<Text>{item.title}</Text>
</View>
</TouchableOpacity>
)}
/>
)
}
export default App;
Also you do not need to using TouchableOpacity if you are using Button Component already.
And since you are using separate component to render item for FlatList so it can be done as below:
// Considering App as Parent Component
const App = () => {
// Considering Item as separate Component
const Item = ({item, index, separators}) => {
return (
<TouchableOpacity
key={item.key}
onPress={() => myFunc('abc')}
>
<View style={{ backgroundColor: 'white' }}>
<Text>{item.title}</Text>
</View>
</TouchableOpacity>
)
}
const myFunc = (args) => {
// perform your action here.
}
return (
<FlatList
data={[{ title: 'Title Text', key: 'item1' }]}
renderItem={Item}
/>
)
}
export default App;
All code are inside App Component;

How to add to export default class extends Component - ({navigation})?

I have a problem with adding the {navigation} parameter to the export default class extends Component, I need it for the FlatList
How can I add it here?
export default class ENews extends Component {
render() {
return (
<View style={styles.main}>
<StatusBar style='auto'/>
<FlatList
data={this.state.data}
onRefresh={() => this.onRefresh()}
refreshing={this.state.isFetching}
initialNumToRender={4}
contentContainerStyle={{ alignSelf: 'stretch' }}
keyExtractor={({ id }, index) => id}
renderItem={({item}) => (
<TouchableOpacity onPress={() => navigation.navigate('FullNews', item)}>
</TouchableOpacity>
)} />
</View>
);
}}
You need to get navigation from props you can either deconstruct this.props and get navigation like this
const { navigation } = this.props;
or you can directly use it like this
this.props.navigation.navigate('FullNews', item)
But I suggest using deconstructing props
export default class ENews extends Component {
render() {
const { navigation } = this.props;
return (
<View style={styles.main}>
<StatusBar style='auto'/>
<FlatList
data={this.state.data}
onRefresh={() => this.onRefresh()}
refreshing={this.state.isFetching}
initialNumToRender={4}
contentContainerStyle={{ alignSelf: 'stretch' }}
keyExtractor={({ id }, index) => id}
renderItem={({item}) => (
<TouchableOpacity onPress={() => navigation.navigate('FullNews', item)}>
</TouchableOpacity>
)} />
</View>
);
}}
Parent class
export default class ComponentName extends Component {
render() {
return (
<>
...other component
<ENews {...this.props}></ENews>
</>
);
}
}
you Component
export default class ENews extends Component {
render() {
const {navigation} = this.props; // here you destructuring you props
return (
<View style={styles.main}>
<StatusBar style="auto" />
<FlatList
data={this.state.data}
onRefresh={() => this.onRefresh()}
refreshing={this.state.isFetching}
initialNumToRender={4}
contentContainerStyle={{alignSelf: 'stretch'}}
keyExtractor={({id}, index) => id}
renderItem={({item}) => (
<TouchableOpacity
onPress={() => navigation.navigate('FullNews', item)}
/>
)}
/>
</View>
);
}
}
change as above in your code

How to access one functional component's state from another functional component in react-native?

I want to access ModalView's state from MarkerView component. Actually i want to see ModalView when i click Get info. button which is in MarkerView. I want to set { setVisiblity(true)} from MarkerView component. How can i do it?
ModalView.js
const ModalView = () => {
const [visiblity, setVisiblity] = useState(false);
return (
<Modal transparent={false} visible={visiblity} >
<TouchableOpacity onPress={() => { setVisiblity(false)}}>
<Text> Submit </Text>
</TouchableOpacity>
</Modal>
)
}
MarkerView.js
const MarkerView = () => {
return (
// i want to set visiblity true from here
<View>
<TouchableOpacity onPress={() => { setVisiblity(true) }}>
<Text>Get info.</Text>
</TouchableOpacity>
</View>
)
}
App.js
import ModalVIew from './components/ModalView';
import Marker from './components/MarkerView';
const App = () => {
return (
<View>
<Marker/>
<ModalVIew/>
</View>
)
}
export default App;
you can use state management like contextAPI or redux, or you can put your state on your higher order component but this will result in some prop drilling.
App.js
const App = () => {
const [visiblity, setVisiblity] = useState(false);
return (
<View>
<Marker visiblity={visiblity} onChangeVisiblity={(val) => setVisiblity(val)}/>
<ModalVIew visiblity={visiblity} onChangeVisiblity={(val) => setVisiblity(val)}/>
</View>
)
}
MarkerView.js
const MarkerView = ({visiblity, onChangeVisiblity: changeVisiblity}) => {
return (
<View>
<TouchableOpacity onPress={() => changeVisiblity(true)}>
<Text>Get info.</Text>
</TouchableOpacity>
</View>
)
}
ModalView.js
const ModalView = ({visiblity, onChangeVisiblity:changeVisiblity}) => {
const [visiblity, setVisiblity] = useState(false);
return (
<Modal transparent={false} visible={visiblity} >
<TouchableOpacity onPress={() => changeVisiblity(false)}>
<Text> Submit </Text>
</TouchableOpacity>
</Modal>
)
}

Passing Navigation to a Function Component

This Is My Home Page Code:
import React from "react";
//More Imports
export default class Home extends React.Component {
//Some Code
render() {
const { navigation } = this.props;
return (
<ScrollView>
//Some Code
<View style={styles.barContainer}>
<Button
title="Add Lesson"
onPress={() => navigation.navigate("ThisLesson")}
/>
</View>
//Some Code
{ScrollViewWithCards}
//Some Code
</ScrollView>
);
}
}
const styles = StyleSheet.create({
//Some Style
});
const cards = [
{
day: "3",
month: "Jan",
numberOfPeople: "4",
time: "17:00-18:00",
title: "Dance Class",
image: require("../../../assets/images/image1.jpeg"),
},
//More Cards...
];
const ScrollViewWithCards = (
<ScrollView>
{cards.map((card, index) => (
<View key={index} style={styles.cardContainer}>
<TouchableOpacity
onPress={() =>
navigation.navigate("ThisLesson", {
image: card.image,
day: card.day,
month: card.month,
time: card.time,
title: card.title,
numberOfPeople: card.numberOfPeople,
})
}
>
<HomeCard
image={card.image}
day={card.day}
month={card.month}
time={card.time}
title={card.title}
numberOfPeople={card.numberOfPeople}
/>
</TouchableOpacity>
</View>
))}
</ScrollView>
);
I'm mapping through an array of static data and rendering cards unto the screen
I made the cards pressable so that they take me to another page,
when I click the card it Returns an error:Reference Error: Can't find variable: navigation
But the Button Above the Cards Works Just Fine
What Am I Doing Wrong?
I tried the useNavigation Hook but it didn't work either
Update
This is my HomeCard component:
import React from "react";
//More Imports
const HomeCard = (props) => {
return (
<View style={styles.container}>
//Some Code
</View>
);
};
export default HomeCard;
const styles = StyleSheet.create({
//Some Style
});
const smallAvatars = [
//Some Array
];
I passed {navigation} to ScrollViewWithCards like so:
const ScrollViewWithCards =({navigation})=>()
but now I'm Getting another Error TypeError: undefined is not an object (evaluating 'navigation.navigate')
Solution
The Solution for this Problem is to transform ScrollViewWithCards to a function component, then pass props to it and add return:
const ScrollViewWithCards = (props) => {
return (
<ScrollView>
{cards.map((card, index) => (
<View key={index} style={styles.cardContainer}>
<TouchableOpacity
onPress={() =>
props.navigation.navigate("ThisLesson", {
image: card.image,
day: card.day,
month: card.month,
time: card.time,
title: card.title,
numberOfPeople: card.numberOfPeople,
})
}
>
<HomeCard
image={card.image}
day={card.day}
month={card.month}
time={card.time}
title={card.title}
numberOfPeople={card.numberOfPeople}
/>
</TouchableOpacity>
</View>
))}
</ScrollView>
);
};
and then in the main render:
<ScrollViewWithCards navigation={this.props.navigation} />
You are setting the const navigation inside the render function, and it wont be accessible inside other functions, so you have to use
this.props.navigation.navigate
Then you can simply do
const ScrollViewWithCards =()=> (
<ScrollView>
{cards.map((card, index) => (
<View key={index} style={styles.cardContainer}>
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate("ThisLesson", {
image: card.image,
day: card.day,
month: card.month,
time: card.time,
title: card.title,
numberOfPeople: card.numberOfPeople,
})
}
>
<HomeCard
image={card.image}
day={card.day}
month={card.month}
time={card.time}
title={card.title}
numberOfPeople={card.numberOfPeople}
/>
</TouchableOpacity>
</View>
))}
</ScrollView>
);
In the routing section, you need to mention the both component like this,
<Stack.Screen name="<your component name>" component={your component class} />
please don't forget to import the files at the above.
and then you can use the navigation props like,
this.props.navigation //for class component
props.navigation //for functional component
or if you have parent child relation in your compoent try this one:
<YOUR_COMPONENT navigation={props.navigation}/> // functional component
<YOUR_COMPONENT navigation={this.props.navigation}/> // class component

React Native Flat list navigate to new screen when pressing on an item

I am new to React Native and I am facing a problem when i try to build a flat list that navigate to another screen with the item details when pressing on a list item.
I am using redux and react-native-router-flux to navigate between screens.
the flat list is rendering but I can not navigate to another screen when I press on item.
here is my code:
class MyListItem extends React.PureComponent {
_onPress = () => {
this.props.onPressItem(this.props.item);
};
render() {
return (
<TouchableOpacity
{...this.props}
onPress={this._onPress}>
<View style={styles.GridViewContainer} >
<Text style={styles.GridViewTextLayout} >{this.props.item.name}</Text>
</View>
</TouchableOpacity>
);
}
}
class CustomersList extends Component {
componentWillMount() {
this.props.getCustomers();
}
componentDidUpdate(prevProps) {
if (prevProps.customers !== this.props.customers) {
this.props.getCustomers();
}
}
_keyExtractor = (item, index) => item._id;
_onPressItem = (item) => {
Actions.customerShow({ customer: item });
};
_renderItem = ({ item }) => (
<MyListItem
id={item._id}
item={item}
onPressItem={() => this._onPressItem(item)}
title={item.name}
/>
);
render = () => {
return (
<View style={styles.container}>
<FlatList
data={this.props.customers}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
extraData={this.state}
numColumns={3}
/>
</View>
);
}
}
const mapStateToProps = (state) => {
const customers = state.customers;
console.log(customers);
debugger
return { customers };
};
export default connect(mapStateToProps, {
getCustomers
})(CustomersList);
And here is the axios api :
export const getCustomers = () => {
debugger;
return (dispatch) => {
dispatch(setCustomerLoading)
axios
.get('https://calm-sands-26165.herokuapp.com/api/customers')
.then(res =>
dispatch({
type: GET_CUSTOMERS,
payload: res.data,
})
)
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: null
})
);
};
}
the routing stacks:
const RouterComponent = () => {
return (
<Router >
<Scene key="root" hideNavBar>
<Scene key="auth">
<Scene key="login" component={LoginForm} title="Please Login" initial />
</Scene>
<Scene key="main">
<Scene
onRight={() => Actions.customerCreate()}
rightTitle="Add"
key="customersList"
component={CustomersList}
title="Customers"
initial
/>
<Scene key="customerCreate" component={CustomerCreate} title="Create Customer" />
<Scene key="customerShow" component={CustomerShow} title="Show Customer" />
</Scene>
</Scene>
</Router>
);
};
Thanx in advance,
The error you are getting is caused when the data source prvided to FlatList is not an array. In your case, this.props.customers is undefined until the function this.props.getCustomers() is returned.
What I would suggest is to use the state to render the flatlist in your CustomersList component. And update the state when the results are returned from the axios async call this.setState({customers : nextProps.customers}) which will rerender the FlatList with the customers array.
class CustomersList extends Component {
constructor(props){
super(props);
this.state = {
customers : []
};
}
...
render = () => {
return (
<View style={{flex:1}}>
{this.state.customers.length > 0 ?
<FlatList
data={this.state.customers}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
extraData={this.state}
numColumns={1}
/> :
<View style={styles.container}>
<ActivityIndicator size={'large'}/>
</View>
}
</View>
);
}
}
As for your navigation, I did test your code myself and it worked :) (As you can see I used <ActivityIndicator /> to render when the list is still fetching.