Nesting React-Native FlatList Components and receiving both items - react-native

I'm trying to display a FlatList of items, with each item containing a FlatList of identical buttons. When one of the buttons is clicked, I want to receive both the parent FlatList item and the value the button corresponds to.
I've tried the following method with no success (timeItem is undefined):
timeSlotsKeyExtractor = (item, index) => item.toString();
timeSlots =
[
'8:00', '10:00', '12:00', '14:00', '16:00', '18:00'
];
renderDate = ({ item }) => {
return (
<View>
<Text>{item.date}</Text>
<FlatList
data={this.timeSlots}
keyExtractor={this.timeSlotsKeyExtractor}
renderItem={({ timeItem }) => (
<TouchableOpacity onPress={() => this.addTimeSlot(item, timeItem)}>
<View><Text>{timeItem}</Text></View>
</TouchableOpacity>
)}
horizontal={true} />
</View>
)
}
addTimeSlot(item, timeItem) {
console.log(item)
console.log(timeItem)
}
I'm aware of SectionLists, and as such also attempted to make each item of my parent FlatList an object containing both the item and the array of timeSlots, however, I wasn't sure how to go about getting the header in the renderItem section:
Screenshot of SectionList code:
<SectionList
sections={this.state.weekDates}
renderSectionHeader={({ section }) => (
<Text>{section.date} </Text>
)}
renderItem={({ item }) => (
<Text>
{iten.date}
{item.timeSlots}
</Text>
)}
keyExtractor={(item, index) => index}
/>
Where am I going wrong with these approaches? I'm fairly new to React Native so there's a good chance I've missed something really simple.

Related

Double tap on a Flatlist React Native

I'm trying to recreate Instagram's double tap on an item and a heart appears for a few seconds. Im currently rendering text items in a flatist (1 item per page) and I want the user to be able to double tap the text section and a heart will appear.
I can't seem to get both the flatList and the animation to work together. Any ideas? In my current code it will only render the flatList and not the Animated.image. I tried wrapping the animated image in an oppress in a touchable without feedback, still no luck
const onDoubleTap = useCallback(() => {
//console.log("tapped");
scale.value = withSpring(1, undefined, (isFinished) => {
if (isFinished) {
scale.value = withDelay(500, withSpring(0));
console.log("Double Tapped");
}
});
}, []);
return (
<TapGestureHandler
numberOfTaps={2}
onActivated={onDoubleTap}
>
<Animated.View>
<FlatList
horizontal={false}
decelerationRate={"fast"}
snapToAlignment={"center"}
snapToInterval={Dimensions.get("screen").height}
data={flatl}
// keyExtractor={(item, index) => `id_${index}`}
style={styles.fullScreen}
renderItem={({ item }) => (
<View style={[{ ...styles.fullHeight }]}>
<Text>{item.name}</Text>
</View>
)}
/>
<AnimatedImage
source={require("../assets/heart.png")}
style={[
styles.image,
rStyle,
]}
resizeMode={"center"}
/>
</Animated.View>
</TapGestureHandler>
);
}

Is there a way to get the Title of the selected item from another component in React Native

I have two different components "HomePage" & "ListItemCA"
In HomePage, I have a FlatList and a modal popup
<FlatList
data={ listData}
keyExtractor={list => list.Title}
renderItem={({ item }) => <ListItemCA data={item} onLongPress={openModal} />}
/>
and each list item is called from another component ListItemCA
function ListItemCA({data, onLongPress}) {
return (
<TouchableOpacity onLongPress={onLongPress} >
<View style={styles.container}>
<Text style={styles.title}>{data.Title}</Text>
<View style={styles.metaContainer}>
<Text style={styles.meta}>{( data.totalMonths != null ? data.totalMonths : '0' )} Months</Text>
<Text style={styles.meta}>{( data.members != null ? data.members.length : '0' )} Members</Text>
</View>
</View>
</TouchableOpacity>
);
}
What I want to acheive?
I want to get the selected list item title on my HomePage component. When a user longpress on a list item that title should be displayed on a modal popup. How do I pass the selected list item title to the HomePage component using longpress?
If your goal is to display data from the long pressed item in the modal, you could add the data as a parameter of your openModal function:
function openModal(data) {
// your function
return (
<Text>{data.Title}</Text>
)
}
Then, in your FlatList, modify the props of ListItemCA to call openModal for the selected item:
renderItem={({ item }) => <ListItemCA data={item} onLongPress={openModal(item)} />}
If you also want to save the data from the long pressed item in your HomePage component for other uses, you could save it in the state. In your HomePage component:
import React, { useState } from 'react'
function HomePage() {
const [itemData, setItemData] = useState()
// your code
}
Then, in your flatlist:
<FlatList
data={listData}
keyExtractor={list => list.Title}
renderItem={({ item }) =>
<ListItemCA
data={item}
onLongPress={ () => {
setItemData(item)
openModal(item)
}}
/>
}
/>
You can achieve this by passing(return) parameter from your component like this -
function ListItemCA({data, onLongPress}) {
return (
<TouchableOpacity onLongPress={() => {
onLongPress(data.Title);
//return data.Title when onLongPressed clicked
}}>
<View style={styles.container}>
...
</View>
</TouchableOpacity>
);
}
then get it in props -
<FlatList
data={listData}
keyExtractor={list => list.Title}
renderItem={({ item }) =>
<ListItemCA
data={item}
onLongPress={(title) => {//this **title** return from **onLongPress(data.Title)**
openModal();
setTitle(title);// or directly can pass that title in openModal func.
}}
/>
}
/>

Conditionally style not working in react native

I followed this answer to dynamically style my component.
Here is my render method :
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.images}
numColumns={2}
keyboardShouldPersistTaps={'always'}
keyboardDismissMode={'on-drag'}
keyExtractor={item => item.localIdentifier}
renderItem={({ item, index }) =>
<TouchableHighlight
underlayColor='transparent'
onPress={() => this.openImage(index)}
onLongPress={() => this.startSelection(item)}
>
<View style={[styles.albumContainer, (this.state.selectedItems.indexOf(item)>-1)?styles.selectedItem:styles.unselectedItem]}>
<Image
style={styles.albumThumbnail}
source={item.image}
/>
</View>
</TouchableHighlight>
}
/>
</View>
);
}
As you can see I am displaying image thumbnail with TouchableHighlight and FlatList. When user will press and hold on any image thumbnail I called startSelection() with particular flatlist item which then add that item to state. I used that state to set style dynamically of my image as :
<View style={[styles.albumContainer, (this.state.selectedItems.indexOf(item)>-1)?styles.selectedItem:styles.unselectedItem]}>
<Image
style={styles.albumThumbnail}
source={item.image}
/>
</View>
Here is startSelection() method :
startSelection(item) {
let temp = this.state.selectedItems;
temp.push(item);
this.setState({
selectedItems : temp
});
}
Here is my stylesheet :
const styles = StyleSheet.create({
selectedItem: {
borderWidth: 3,
borderColor: '#22aaff',
},
unselectedItem: {
borderColor: '#000000',
}
});
But when user press and hold that view, item will added to state but style is not changing.
Please help me what's going wrong here !!!
This can be found on FlatList docs:
This is a PureComponent which means that it will not re-render if props remain shallow-equal. Make sure that everything your renderItem function depends on is passed as a prop (e.g. extraData) that is not === after updates, otherwise your UI may not update on changes. This includes the data prop and parent component state.
So you can add extraData to your FlatList component like this:
FlatList Component:
<FlatList
data={this.state.images}
extraData={this.state} //add this!
numColumns={2}
keyboardShouldPersistTaps={'always'}
keyboardDismissMode={'on-drag'}
keyExtractor={item => item.localIdentifier}
renderItem={({ item, index }) =>
<TouchableHighlight
underlayColor='transparent'
onPress={() => this.openImage(index)}
onLongPress={() => this.startSelection(item)}
>
<View style={[styles.albumContainer, (this.state.selectedItems.indexOf(item)>-1)?styles.selectedItem:styles.unselectedItem]}>
<Image
style={styles.albumThumbnail}
source={item.image}
/>
</View>
</TouchableHighlight>
}
/>
P.S: If your component state has variables which should not re-render FlatList, you would be better of using extraData = {this.state.selectedItems}, but then you need to make sure you pass a different reference to selectedItems when you call setState on startSelection. Like this:
startSelection(item) {
let temp = [...this.state.selectedItems];
temp.push(item);
this.setState({
selectedItems : temp
});
}
Wrap them with extra []
style={[styles.albumContainer, [(this.state.selectedItems.indexOf(item)>-1)?styles.selectedItem:styles.unselectedItem]]}

Algolia and React Native FlatList ListHeaderComponent

I you put an Algolia connected component in a header of a FlatList it's as if it enters an infinite loop of queries. The connectInfiniteHits runs constantly.
This is really annoying if you like to put some simple filters in the headers of a list of hits.
My setup is like this:
I have a FlatList that is wrapped by the connectInfiniteHits HOC.
The ListHeaderComponent contains a component this is wrapped by the connectRefinementList HOC. The same problem occurs with a connectSearchBox HOC.
Has anyone seen this and found a solution?
I manage to make it work with those lines:
const RefinementList = connectRefinementList(({ items, refine }) => (
<View>
{items.map(item => (
<TouchableOpacity key={item.label} onPress={() => refine(item.value)}>
<Text style={{ fontWeight: item.isRefined ? '600' : 'normal' }}>
{item.label}
</Text>
</TouchableOpacity>
))}
</View>
));
const InfiniteHits = connectInfiniteHits(({ hits, hasMore, refine }) => (
<FlatList
data={hits}
keyExtractor={item => item.objectID}
onEndReached={() => hasMore && refine()}
ListHeaderComponent={<RefinementList attribute="brand" />}
renderItem={({ item }) => (
<View>
<Text>{JSON.stringify(item).slice(0, 100)}</Text>
</View>
)}
/>
));
Note that I'm not using the function version which indeed breaks.

Make VirtualizedList show as Grid

I'm trying to make something like this:
The problem: The project was built with immutablejs and according to React Native Docs, I can't use FlatList thus I can't use numColumns props feature of that component.
AFAIK, my only choice is to use VirtualizedList as the docs points out, but I can't figure out how to display the cells as a grid as shown above.
I've already tried to add style props in both cell and view wrapper, but none of the code used to align the cells, like the picture I posted, is ignored. In fact it was showing perfect when I was using ScrollView, but due the HUGE lag I'm moving the code to VirtualizedList.
Any help? Anything would be welcome, I already digged a lot on Google but I can't find anything about this.
Some sample code:
<View>
<VirtualizedList
data={props.schedules}
getItem={(data, index) => data.get(index)}
getItemCount={(data) => data.size}
keyExtractor={(item, index) => index.toString()}
CellRendererComponent={({children, item}) => {
return (
<View style={{any flexbox code gets ignored here}}>
{children}
</View>
)}}
renderItem={({ item, index }) => (
<Text style={{also here}} key={index}>{item.get('schedule')}</Text>
)}
/>
</View>
Answering my own question:
I got it working by copying the FlatList.js source code from react-native repo.
Here's an example code:
<VirtualizedList
data={props.schedules}
getItem={(data, index) => {
let items = []
for (let i = 0; i < 4; i++) {
const item = data.get(index * 4 + i)
item && items.push(item)
}
return items
}}
getItemCount={(data) => data.size}
keyExtractor={(item, index) => index.toString()}
renderItem={({item, index}) => {
return (
<View key={index} style={{flexDirection: 'row'}}>
{item.map((elem, i) => (
<View key={i}>
<Text key={i}>{elem.get('horario')}</Text>
</View>
))}
</View>
)
}}
/>
The number 4 is for the number of columns. The key parts are in the getItem and adding flexDirection: 'row' at renderItem in the View component.