Hi my custom component is wrapped in TouchableOpacity like this.
const profileOnClick = () => {
console.log('Card Clicked!');
};
export const InfluencerCard = props => {
const {influencer, navigation} = props;
return (
<TouchableOpacity onPress={() => profileOnClick()}>
<Card>
<Text>
{influencer.user.name}
</Text>
<Text>
{influencer.tags[0].name}
</Text>
</Card>
</TouchableOpacity>
);
};
In Homescreen
<ScrollView>
{data.categoriesForHome.map(category => (
<Layout key={category.id}>
<Text>
{category.name}({category.total})
</Text>
<ScrollView horizontal={true}>
{category.influencerProfiles.map(profile => (
<View key={profile.id}>
<InfluencerCard influencer={profile} />
</View>
))}
</ScrollView>
</Layout>
))}
</ScrollView>
When I clicked my custom component InfluencerCard, it doesn't do anything.
I wonder it is because that component is in other component, so parent component block clicking on custom component. Or calling onPress function is wrong.
But I tried without parent component, it was working.
What am I missing?
It was my mistake. The problem was not from code or components.
I use Card component from #ui-kitten/components and it implements TouchableOpacity behind the scene. So I don't need to wrap with TouchableOpacity again.So just do
<Card onPress={() => profileClick()}></Card>
Related
const Screen = () => {
return (
<View style={styles.container}>
<Text style={styles.heading}>Screen Data</Text>
<View>
<FlatList
data={data}
renderItem={({item}) => (
<View>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.description}>{item.description}</Text>
</View>
)}
ListFooterComponent={() => <Buttons text={''} onPress={undefined} />}
/>
</View>
</View>
);
};
export default Screen;
I have created a Button component which always come at the end of flatlist which is scrollable but it gets overlap I want it to keep at end of content.
List footer component is made for that it can be any component, check docs https://reactnative.dev/docs/flatlist#listfootercomponent.
I am using React Native FlatList and React Native Modal.
Upon clicking on the item from the FlatList, I want to view 1 Modal only (containing the details of the item selected).
However, if there are 4 items in the FlatList, selecting 1 item causes
all 4 modals to pop up.
Is there anyway I can display only 1 modal for 1 selected item in the FlatList instead of multiple modal?
Code Snippet below (some lines of code were removed as it's not needed):
constructor(props) {
super(props);
this.state = {
dataSource: [],
isLoading: true,
modalVisible: false,
}
}
setModalVisible = (visible) => {
this.setState({ modalVisible: visible });
}
viewModal(item, price) {
const { modalVisible } = this.state;
return (
<Modal
statusBarTranslucent={true}
animationType={"slide"}
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
}}
>
<View>
<View>
<View>
<Text>
Appointment Start Time:
</Text>
<Text>
{moment(item.time_start).format('h:mm a')}
</Text>
</View>
<View>
<Text>
Appointment End Time:
</Text>
<Text>
{moment(item.end_start).format('h:mm a')}
</Text>
</View>
<View style={styles.row}>
<Text>
Total:
</Text>
<Text>
{price}
</Text>
</View>
<View>
<View>
<Button
mode="outlined"
onPress={() => {
this.setModalVisible(!modalVisible);
}}
>
{'Cancel'}
</Button>
</View>
<View>
<Button
mode="contained"
onPress={() => {
this.setModalVisible(!modalVisible);
}}
>
{'Accept'}
</Button>
</View>
</View>
</View>
</View>
</Modal>
);
}
viewFreelancerTime() {
return (
<View>
<FlatList
renderItem={({ item }) => {
let totalPrice = (parseFloat(item.service_price) + parseFloat(item.service_deposit)).toFixed(2);
return (
<Container>
{this.viewModal(item, totalPrice)}
<TouchableNativeFeedback
onPress={() => {
this.setModalVisible(true);
}}
>
<View>
<View>
<Text>
{moment(item.time_start).format('h:mm a')}
</Text>
</View>
<View>
<Text>
{totalPrice}
</Text>
</View>
</View>
</TouchableNativeFeedback>
</Container>
);
}}
/>
</View>
);
}
render() {
return (
<>
<View style={{ flex: 1 }}>
{this.viewFreelancerTime()}
</View>
</>
);
};
The poblem is that you are rendering the modal in the renderItem method, so every time you select an item, the modal will open in each rendered item.
To solve that you will have to render a custom Modal component with an absolute position at the same level of your FlatList, and pass the selected item information as props.
UPDATE
Just something like this:
import React, {useState} from "react";
import { Modal } from "react-native";
export default function MyFlatList(props) {
const [selectedItem, setSelectedItem] = useState(null);
const handleOnSelectItem = (item) => {
setSelectedItem(item);
};
const handleOnCloseModal = () => {
setSelectedItem(null);
};
renderItem = ({ item }) => {
return (
<Container>
<TouchableNativeFeedback onPress={() => handleOnSelectItem(item)}>
<View>
<View>
<Text>{moment(item.time_start).format("h:mm a")}</Text>
</View>
<View>
<Text>{totalPrice}</Text>
</View>
</View>
</TouchableNativeFeedback>
</Container>
);
};
return (
<View>
<FlatList renderItem={this.renderItem} />
<CustomModal isVisible={selectedItem} selectedItem={selectedItem} onClose={handleOnCloseModal} />
</View>
);
}
export function CustomModal(props) {
const { isVisible, item, onClose, /*...*/ } = props;
// Play with the item data
let totalPrice = (
parseFloat(item.servicePrice) + parseFloat(item.serviceDeposit)
).toFixed(2);
return <Modal visible={isVisible} onRequestClose={onClose}>{/*...*/}</Modal>; // Render things inside the data
}
I suggest you to do a pagination and play with FlatList native props if you are going to implement an infinite scroll.
Pd: to reduce re-renders because of state updates, I am reusing the selectedItem state, so if it is not null then the modal will be visible
I'm calling a component in my file so I made a function in parent component and want to hit this function onPress of child component MenuItem and I also want to hit hideMenu function from child component at same time. Please provide me a solution for it. Thanks
Parent Component
onView(){
alert('Dairu');
}
{this.state.clientsList.map((item) => {
return (
<View style={styles.caseItem} key={item.ID}>
<Card style={styles.card}>
<CardItem>
<Body>
<View style={styles.rowTitle}>
<Text style={styles.title}>{item.FullName}</Text>
<CustomMenu onView={() => this.onView()} />
</View>
<View>
<Text style={styles.lbl}>Email: <Text style={styles.lblValue}>{item.EmailID}</Text></Text>
<Text style={styles.lbl}>Client Type: <Text style={styles.lblValue}>{item.ClientType}</Text></Text>
</View>
</Body>
</CardItem>
</Card>
</View>
);
})}
Child Component
hideMenu = () => {
this._menu.hide();
};
render() {
return (
<Menu
ref={this.setMenuRef}
button={<Icon type="Feather" name="more-vertical" onPress={this.showMenu} style={{ fontSize: 20, color: '#555' }} />}
>
<MenuItem onPress={this.props.onView}>View</MenuItem>
<MenuItem onPress={this.hideMenu}>Edit</MenuItem>
<MenuItem onPress={this.hideMenu}>Delete </MenuItem>
</Menu>
);
}
You've already passed the onView prop to the child object. Just call this method in the hideMenu method in the child component:
hideMenu = () => {
this._menu.hide();
this.props.onView();
};
you just need to add this.props.onView() on your hideMenu function
hideMenu = () => {
this._menu.hide();
this.props.onView();
};
also, you can pass an argument to parent component like:
this.props.onView(arg1, arg2);
and in parent component:
onView={(arg1, arg2)=> onView(arg1, arg2)}
So I am trying to build an app similar to a blog. And I am not sure how to render the single post that has been chosen to read.
<TouchableWithoutFeedback onPress={ () => this.props.navigation.navigate('Unique')}>
<Text style={styles.titleStyle}>{post.title}</Text>
</TouchableWithoutFeedback>
^This is the event that navigates me to the post I have clicked (not sure how to pass its props or id)
And this is the component I am redirected to:
class Page extends Component {
render(){
return(
<ScrollView>
<Query query={UniqueNews}>
{({data, loading})=> {
if(loading) return <Text>Loading</Text>;
const {posts} = data;
return posts.map(post=>
<OneNews key={post.id} post={post} />
)
}}
</Query>
</ScrollView>
)
}
}
const UniqueNews = gql `query{
posts{
title
author
date
id
body
image{
url
}
}
}
`
OneNews =
<View style={styles.containerStyle}>
<View style={styles.cardSectionStyle}>
<Text>{title}</Text>
<Text>{author}</Text>
<Text>{body}</Text>
</View>
</View>
When I render or navigate to that screen, every post is shown instead of the one I have tapped on. How do I set this up? My expected output is to just see one of the posts (being the chosen one by tapping on it) and render it. Thanks
I found a way to solve this:
First I will pass a param while navigating to the Unique component :
<TouchableWithoutFeedback onPress={ () => this.props.navigation.navigate('Unique', {id: post.id})}>
<Text style={styles.titleStyle}>{post.title}</Text>
</TouchableWithoutFeedback>
After that, I will simply make a conditional render inside that Unique component:
<ScrollView>
<Query query={singleNews}>
{({data, loading})=> {
if(loading) return <Text>Loading</Text>;
const {posts} = data;
return posts.map(post=>
{if(this.props.navigation.state.params.id===post.id)
return <View>
<OneNews key={post.id} post={post} />
</View>}
)}}
</Query>
</ScrollView>
And that's it.
I have this code:
<View style={{flex:1}}>
<ScrollView>
<View style={{flex:0.25,elevation:2,marginVertical:5}}>
<MoviesChild/>
</View>
<View style={{flex:0.75}}>
<FlatList
data={ListOfMovies}
renderItem={this.renderData.bind(this)}
keyExtractor={(x, i) => x.id}
extraData={this.state}
/>
</View>
</ScrollView>
</View>
The problem is, my FlatList's data appears but my child component MoviesChild disappears. Maybe Flatlist overlaps the child component. Could someone figure out where I'm doing a mistake please?
You should give style to ScrollView with flexGrow:1
I suggest you not to use FlatList inside a ScrollView, 'cause FlatList scroll itself. So, in order to achieve your objective, wrap your first View in a function and pass it to the ListHeaderComponent property of your FlatList, like this:
<View style={{flex:1}}>
<View style={{flex:0.75}}>
<FlatList
data={ListOfMovies}
renderItem={this.renderData.bind(this)}
keyExtractor={(x, i) => x.id}
extraData={this.state}
ListHeaderContainer={this.renderHeader}
/>
</View>
</View>
where
private renderHeader = () => {
return(
<View style={{flex:0.25,elevation:2,marginVertical:5}}>
<MoviesChild/>
</View>
)
}
I hope this can fix your issue!
You can find what I'm talking about on the official doc
Try this,
<View style={{flex:1}}>
<View style={{ flex:0.25,elevation:2,marginVertical:5 }}>
<MoviesChild/>
</View>
<FlatList
style={{flex:0.75}}
data={ListOfMovies}
renderItem={this.renderData.bind(this)}
keyExtractor={(x, i) => x.id}
extraData={this.state}
/>
</View>