I created a dashboard for work that involves A SectionList and a filtering search input box (TextInput). Only targeting Android platform.
<SectionList
ref={listRef}
style={{ paddingHorizontal: basePadding }}
sections={filterResults ?? data}
keyExtractor={(item) => item?.Id}
renderItem={(item) => (
<Items onPressHandler={onDeviceListItemPress} item={item} />
)}
renderSectionHeader={({ section }) => <Header section={section} />}
ListFooterComponent={() => <View style={{ paddingVertical: 50 }} />}
stickySectionHeadersEnabled
/>
The search string is used to filter all the items in the list (obviously).
The issue is that when:
the user has scrolled the list with the sticky header partially covering it's items,
Then filters out all the items
Then clears the search string (rendering the list in it's initial state)
The section header breaks, and returns to the position it was in before the filter occurs.
Once the user interacts with the SectionList the problem is solved, but I want to avoid this issue all togethor.
I'm thinking it's because the sticky header location is being cached somewhere and then when the list is being re-rendered it is going off that value. Any ideas on how to restore that state?
I've tried:
Forcing a scroll to top
Searched for possible props to solve the issue
Related
Basically, I have a list of items (with images) inside a single Card (custom component). Because the rendering of those items is slow, I wanted to use a FlatList to render them incrementally.
Unfortunately, I get the expected error
VirtualizedLists should never be nested inside plain ScrollViews ...
But I don't actually want to use a ScrollView inside the Card. I just want to render a few Items in a single Card, which should change its size to fit all the items.
Setting scrollEnabled={false} on the FlatList still shows the error above.
Using the ListHeaderComponent and ListFooterComponent props is not an option, because the content above and below should NOT be rendered inside the Card.
Here is a minimal example of what I mean:
const Comp = () => {
return (
<ScrollView contentInsetAdjustmentBehavior="automatic">
<Text>Header</Text>
<Card>
<FlatList
data={data}
renderItem={({ item }) => (
<Image source={{uri: item.localImageUrl}}/>
)}
keyExtractor={(item) => item.id}
scrollEnabled={false}
initialNumToRender={0}
maxToRenderPerBatch={3}
contentInsetAdjustmentBehavior='automatic'
/>
</Card>
<Text>Footer</Text>
</ScrollView>
);
};
What's interesting though, that aside from the error - I get the result I expected, and I could technically hide that error and ignore it, but that does not seem like the recommended approach.
Important: I am not specifically looking for a FlatList solution. It could technically be any Component that renders items incrementally in a non-blocking way.
The important point with a Flatlist is the reusing of cells so that not all components need to be rendered at the same time. So scrolling is an important part of this. On the other hand two scrollable components inside eachother will make it impossible for the system to know which component should be scrolled.
If there are only 3 items and it should not be scrollable you can just include a list of items inside the Scrollview like this:
const Comp = () => {
return (
<ScrollView contentInsetAdjustmentBehavior="automatic">
<Text>Header</Text>
<Card>
{ data.map((item, index) => {
return (
<Text key={index}>{item.title}</Text>
);
}) }
</Card>
<Text>Footer</Text>
</ScrollView>
);
};
In my React Native Project, I am trying to make some kind of lists using Flatlist or sometimes using map method, for executing JSX Element. I am getting the result correctly, But there is a bit of a problem in separate.
Let's take chatting app example, When we open WhatsApp there are a lot of people showing up, but there is also a tiny separator after each item, That looks great, Now exactly when I try to put that separator in my React Native application using ItemSeparatorComponent attribute in Flatlist, It's working but still in some places, meaning in some items that separator not showing up, its looks missing, it feels that there is no border/separator. And actually what's going on is that, the below item from that separator which is hidden or which height looks smaller than others, that below View go a little bit towards the upside, so the separator gets to hide, That's the main problem, Why is that happening, I tried everything but still, I am getting that UI problem.
Here is code example:
<FlatList
data={actionSheet._data}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
keyExtractor={(_, index) => index}
renderItem={({item, index}) => <ActionSheetClickableItem data={item} index={index}/> }
ItemSeparatorComponent={() => (
<View
style={{
height: 1,
width: '100%',
backgroundColor: 'red'
}}
/>
)}
/>
OR
<ScrollView>
{
actionSheet._data.map((item, index) => (
<>
<ActionSheetClickableItem data={item} index={index} key={index}/>
<View
style={{
height: 1,
width: '100%',
backgroundColor: 'red'
}}
/>
</>
))
}
</ScrollView>
So according to the above code, I know for sure, that everything is correct, But why is that separator get hidden, If we look at this picture in the area of the green rectangles, there is no border showing up, Why... I want to show it here, I tried to put zIndex property, that trick working correctly but that isn't the solution, It have to correct view as we expecting, why its behave like this, any solution??????
I was facing the same issue.
That might be with Emulator or Your screen.
You can use also increase the height of itemSeparater or make the background color more darker.
If you check it in real device then it displays all the separaters.
In react-native, using SectionList, I want to have a sticky section header with a background color, but only when the element is sticky. What I've done so far, is to set a views position to absolute. The problem is that I can't find a way to get the items behind the view, unlike the picture nested below:
Without zIndex'es applied:
Until now, I've tried to set different zIndex'es, but it doesn't seem to work, as everything just goes behind the view including the SectionHeaders, despite having a higher zIndex, than the view. I guess, it's because, the zIndex is relative to the parent. A picture is nested below:
With zIndex'es applied:
To sum up: How do I make sure that the items goes behind the view, to simulate a background, on the sticky section header? Other solutions to get a background on the sticky section header, is also much appreciated.
I have inserted the code (with zIndex'es) below.
render () { return (
<View>
{/* Header */}
<View style={[styling.headerContainer, {zIndex: 8}]}/>
{/* List */}
<SectionList
sections={DATA}
keyExtractor={(item, index) => `${item} - ${index}`}
stickySectionHeadersEnabled
style={{marginTop: 44}}
// Section Header
renderSectionHeader={({section}) =>
<View style={[styling.sectionHeaderContainer, {zIndex: 9}]}>
<Text style={styling.title}>{section.title}</Text>
</View>
}
// Items
renderItem={({item}) =>
<Text style={[styling.text, {zIndex: 7}]}>{item}</Text>
}
/>
</View>
)}
I have recently been learning react native and I am using expo. I want to make a responsive grid of clickable items. Similar to how an iphones main screen has a bunch of apps on it in a grid. My attemps so far have gotten me this:
For what you see in the image I am using a flat list with a fixed number of columns (hence one of the columns being cut off). The items in the list do not respond to clicks.
How should I go about making this into a responsive grid that adjusts to different phones screen sizes, have it scrollable (which it currently is) and have each item in the list respond to being pressed?
I appreciate anything pointing me in the right direction
you can do it in two ways.
1. by using ant rn design use "GRID COMPONENT"
<Grid
data={data}
columnNum={3}
isCarousel
onPress={(_el, index) => alert(index)}
/>
**Sample** : [Vist][1]
for more [visit here][1] .
By Using "FlatList"
<FlatList
data={[{},{}]} //array of objects
renderItem={({item}) => (
<TouchableOpacity
style={style.GridViewBlockStyle}
onPress={this._handleClick.bind(this, item.link, item.text)}
>
<View
style={{
height: 100,
justifyContent: 'center',
alignItems: 'center',
}}
>
{item.icon}
<Text style={style.GridViewInsideTextItemStyle}> {item.text}
</Text>
)}
</View>
</TouchableOpacity>
)}
numColumns={3}
keyExtractor={item => item.id.toString()}
style={{}}
/>
Sample Visit.
Does anyone know how I can include the header of a flatlist in the columns?
For example, in the image below I would like "Design" to follow "New +" in the same row.
Example Screenshot
Here is my code in render:
<FlatList
data={goalItems}
extraData={this.props}
renderItem={this._renderGoal}
ListHeaderComponent={this._renderGoalHeader}
keyExtractor={item => item.goal_id}
showsVerticalScrollIndicator={false}
numColumns={2}
/>
and in _renderGoal:
_renderGoal = ({ item }) => (
<TouchableOpacity
onPress={() => this.openOptions(item)}
style={{ padding: 10 }}>
<Bubble>
<Text style={[styles.normalText]}>{item.name}</Text>
<View style={{ alignItems: 'center' }}>
<Text>{formatSeconds(item.total_time)}</Text>
</View>
</Bubble>
</TouchableOpacity>
);
and in _renderGoalHeader:
_renderGoalHeader = () => (
<TouchableOpacity
onPress={() => this.openAddGoal()}
style={{ padding: 10 }}>
<Bubble style={[styles.newGoal]}>
<Text style={[styles.touchableText]}>New +</Text>
</Bubble>
</TouchableOpacity>
)
Thanks in advance.
As a temporary fix, I removed numColums from the flatList and added the following:
contentContainerStyle={{ flexWrap: 'wrap', flexDirection: 'row', width: 345 }}
I would love to be proven wrong here, but as far as I can tell, this cannot be done using numColumns. There is a workaround which I'll list at the end, but first the reasoning:
Explanation
I ran into this same issue but with the footer and did a code dive into the React Native source to see what was happening. First thing I did was use the inspector and I noticed that the header and footer of a FlatList are wrapped in a View while each row of columns is wrapped in CellRenderer > View. This is the first clue that FlatList does not account for headers and footers in numColumns. This is problematic because FlatList does not support masonry type layouts. This hints at the fact that there are many assumptions being made about how they are handling rendering the rows - that is, items are separated into rows to be rendered.
With these clues, I went into the source and found that FlatList passes its own custom renderItem down to VirtualizedList. Meanwhile, ListHeaderComponent and ListFooterComponent are passed down as is. They are never used in FlatList. Not a good sign as you'll see that the aforementioned _renderItem is what handles grouping the items together into columns to render using flexDirection: 'row' and whatever is passed down with columnWrapperStyle.
But perhaps VirtualizedList is smart enough to combine the header and footer into data that gets passed into renderItem?
Sadly, this is not the case. If you follow the code in VirtualizedList, you'll eventually see all the logic get played out in render() where:
a cells array is created
that first has the header pushed into it
followed by all of the data pushed into it via its _pushCells helper method
This is where CellRenderer appears;
hence why headers and footers are not wrapped by this whereas the rows are.
This is also where FlatLists _renderItem is used explaining why you get numColumns number of items wrapped in a CellRenderer.
and then finished with the footer being pushed in the same manner as the header.
This cells array of React components is then eventually cloned which helps with things such as keeping scroll position.
And finally rendered.
Do to this implementation, you will always get a fixed CellRenderer component which prevents headers and footers from being included as part of numColumns.
Workaround (not recommended)
If you've used React Native prior to FlatList being released, you'll know that one old workaround was to be very careful with your dimensions and use flexWrap: 'wrap'. It's actually documented within the source code as it will spit out a warning if you use this method of handling columns. Do note that it is NOT recommended to do this (I believe performance and perhaps scroll position may be some of the reasons against using this method).
If you are willing to do this though, add these styles to your contentContainerStyle:
flexDirection: 'row',
flexWrap: 'wrap',
// You'll probably also want this for handling empty cells at the end:
justifyContent: 'flex-start',
and remove the numColumns prop. Then make sure to manually size your component dimensions to get the correct looking column style layout you desire. This should allow your header and footer to render as part of the columns.
If you want to do it with numOfColumns, than use flexDirection:'row' within your header component
_renderGoalHeader = () => (
return(
<View style={{flexDirection:'row'}}>
<TouchableOpacity
onPress={() => this.openAddGoal()}
style={{ flex:1, padding: 10, }}>
<Bubble style={[styles.newGoal]}>
<Text style={[styles.touchableText]}>New +</Text>
</Bubble>
</TouchableOpacity>
<View>
<View style={{flex:1}}/> //this view is just to cover half width
);
)