React-native FlatList items not get to right height - reactjs

I have items in diffrent heights (250 or 150) inside a FlatList,
When iterate each item and append the state of the dataSrouce for the FlatList everything renders alright, but if I want to avoid the "appending" affect and set the dataSrouce to all of the items at once, it seem FlatList have a wierd bug where items are not getting their right height (There is a blank space on the botton where the item had suppose to fill it).
Tried put "flexGrow:1" on the FlatList, tried the "initialNumToRender" property,
Tried to fix height of the each Item in the view.
Container of the FlatList is "flex:1".
My FlatList:
render() {
const _this = this;
const { loading } = this.state;
return (
<Components.ViewContainer>
{this.printTopHeader()}
{loading ? (
<ActivityIndicator size={25} />
) : (
<FlatList
style={{ flex: 1 }}
removeClippedSubviews={true} //tried with and without
data={this.state.posts}
extraData={this.state.posts} //tried with and without
renderItem={({ item }) => (
<HomeCard
post={item}
/>
)}
keyExtractor={(item, index) => item.key}
/>
)}
</Components.ViewContainer>
);
}
Components.ViewContainer:
const ViewContainer = styled.View`
flex:1;
`;
HomeCard:
render() {
const { theme, showActions } = this.props;
const {
imageUrl,
user,
title,
selectedPlace,
textColor,
backgroundColor
} = this.props.post;
return (
<Components.ContainerView>
...
</Components.ContainerView>
);
}
export default withTheme(HomeCard); // styled-components implementation

The issue were caused by a matter of wrong styling applied to the child elements,
By better understand how FlexBox works I managed to figure that I was missing a flex: 1 attribute on the list elements, and therefore the items styling didn't calculated correctly.

Related

React virtualized infiniteLoader scrolling to top on load using List

I am currently using react-virtualized InfiniteLoader component to scroll over a List of elements that could be variable all the time.
This is my component now:
<VirtualizedInfiniteLoader isRowLoaded={isRowLoaded} loadMoreRows={loadMoreRows} rowCount={rowCount}>
{({ onRowsRendered, registerChild }) => (
<AutoSizer>
{({ width, height }) => (
<List
key={`${width}${height}${rowCount}`}
height={height - headerHeight}
onRowsRendered={onRowsRendered}
scrollToIndex={scrollToIndex}
scrollToAlignment={scrollToAlignment}
ref={registerChild}
rowCount={rowCount}
rowHeight={rowHeight}
rowRenderer={rowRenderer}
width={width}
onScroll={onListScroll}
/>
)}
</AutoSizer>
)}
</VirtualizedInfiniteLoader>
And i'm using this in a container:
renderInfiniteLoader = currentStatus => {
const { cache, headerHeight } = this.state;
const { scrollToIndex, params, allTasks, selectedFilterFields, searchFilters } = this.props;
const list = allTasks[currentStatus];
console.log(list.length, params);
return (
<InfiniteLoaderWrapper
diffHeight={marginBottomToAdd({ searchFilters, selectedFilterFields, customerID: params.customerID })}
ref={this.cardsContainer}
>
<InfiniteLoader
loadMoreRows={options => this.loadMoreRows(options, status)}
rowRenderer={this.renderTasks(list, currentStatus)}
isRowLoaded={this.isRowLoaded(list)}
scrollToIndex={scrollToIndex}
scrollToAlignment="start"
rowCount={list.length + 1}
headerHeight={150}
deferredMeasurementCache={cache}
rowHeight={cache.rowHeight}
onListScroll={this.onInfiniteLoaderScroll}
/>
</InfiniteLoaderWrapper>
);
};
The problem here is that everytime i scroll down and a loader appear if i'm scrolling too fast, page scroll to first element on top.
The prop scrollToIndex seems to be 0 everytime so probably that's the problem.
If i remove that prop, scrolling works correctly, but comes up another issue:
I can click on list's element to go to a detail page, and if i come back i expect to be in the correct position of the list, but i'm on top (again, because scrollToIndex is 0).
What is the correct value to pass to scrollToIndex to be always in the right point?
I can see there was this bug in some version of react-virtualized but can't find any workaround.
Thanks in advance!

FlatList, Props ListHeaderComponent and Dynamic flatlist data

Sorry for the title, I wasn't inspired, and I didn't know what title to put for my problem.
I'm using Socket.io on my React native application to render some dynamics datas.
I'm using FlatList to display all this datas, and I need to show some contents like filters and buttons at the top of the FlatList.
So In my FlatList Props, I used ListHeaderComponent and in this component I display one other flatlist and contents.
My problem is :
When the main FlatList is updated with one of the Socket.io response (Every 5sec), all the components displayed by ListHeaderComponent aren't usable for a bit of time.
This period is like 500ms or less, but in the ListHeaderComponent I have some buttons / filters, and they are not clickable during this period.
And the problem is repeated each time the FlatList data is updated.
You can see my code (simplyfied) here :
export default function MyList({ navigation }) {
const [myData, setMyData] = useState<ResponseType[]>([])
// Socket Io
const socket = io(SOCKET_URL, { forceNew: true, timeout: 5000 })
useEffect(() => {
socket.on("connect", () => {
console.log("Connected to Socket ID : ", socket.id)
})
return () => {
socket.disconnect()
socket.close()
}
},[])
socket.on("socketResponse", (data: ResponseType) => {
setMyData(prevDate => [...prevDate, data])
})
const renderFilterItem = ({ item }: { item: Filter }) => <Text key={ item.id }>{ item.title }</Text>
const renderListItem = ({ item }: { item: ResponseType }) => <Text key={ item.id }>{ item.id }</Text>
const ListTop = () => (
<>
<TouchableOpacity onPress={() => navigation.navigate('HomePage')}>
<Text>BACK CONTENT</Text>
</TouchableOpacity>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={someFilters}
keyExtractor={(item) => item.id}
renderItem={renderFilterItem}
/>
</>
)
return (
<FlatList
ListHeaderComponent={ListTop}
data={myData}
keyExtractor={(item) => item.id}
renderItem={renderListItem}
maxToRenderPerBatch={5}
windowSize={2}
/>
)
}
I don't know how to explain better my problem. But If I move the component of ListHeaderComponent above the FlatList, there is no more problem.
But If I do this, just the FlatList is scrollable, and I want all the page scrollable.
I can't wrap my Flatlist with a ScrollView because of this error : VirtualizedLists should never be nested inside plain ScrollViews with the same orientation
Has anyone faced this problem ?
Thanks
try to use optimization for ListTop component as a possible problem here would be rendering of your functional component due to change of parent one.
here is the code sample.
import React, {useCallback} from 'react';
// your other codes
const ListTop = useCallback(() => (
<>
<TouchableOpacity onPress={() => navigation.navigate('HomePage')}>
<Text>BACK CONTENT</Text>
</TouchableOpacity>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={someFilters}
keyExtractor={(item) => item.id}
renderItem={renderFilterItem}
/>
</>
), [someFilters]);
here the component will be memoized until there is no change in someFilters.

FlatList rendering is heavy for big data set

In my application I have a FlatList with dataset of 100 items in it. Each item has a complex UI and I noticed that it's taking a heavy toll on performance. The page that has the list takes up to 5 seconds to load.
I noticed that the moment the component is rendered for the first time, the renderItem function of the FlatList is also called for each and every item in my data set, I also noticed that it also happens if there any other setState change for other stuff on that page. Is there a way to prevent this re-rendering of the flat list or at least to re-render only the visible items - just like with Recycle with in native Android?
How can I only render the visible items when the component first appears?
I tried to use initialNumToRender and maxToRenderPerBatch but neither have worked.
Here is an example of the code:
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const App = () => {
const [text, setText] = React.useState('')
const renderItem = ({ item }) => {
console.log("Render Item")
return (<Item title={item.title} />)
};
return (
<SafeAreaView style={styles.container}>
<TextInput
value={text}
onChangeText={(val) => setText(val)}
/>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
initialNumToRender={6}
maxToRenderPerBatch={6}
/>
</SafeAreaView>
);
}
If I try to type something in TextInput the whole list re-renders but nothing in the list has changed.. how can I prevent this?

React native re-render causes view to scroll to top - Do my keys change between renders?

I am using react native, redux and flatlist. I render 10 items and then fetch / render the next 10 items on click (load more). My problem is that when I use setState(to increase the page counter) (thats why I dont use setstate in my code) to fetch more items (i use pagination) or ask whether or not Im fetching (true when fetching -> shown loading sign, false when fetched -> showing items), that triggers
a re-render and my view gets scrolled to top.
Ive been reading for a while now and event.preventDefault (as sometimes suggested) doesnt work and I do believe that my problem is the key / parent tree of the corresponding element changes between renders. I do give each item the key of its ID which is always unique. Am I doing it wrong? Or what else could be causing this? Thanks!
class App extends React.Component {
state = {
page : 0
}
componentDidMount() {
this.props.fetchData(0)
this.state.page++;
}
load = () => {
this.props.fetchData(this.state.page);
this.state.page++;
}
renderRow = ({item}) => {
return (
<Item
key={item.id}
coin={item}
/>
)
}
renderButton() {
return <Button
title="Load more"
onPress={this.load}
/>
}
render() {
if (this.props.isFetching) {
return (
<View>
<Text>Loading</Text>
</View>
)
}
return (
<View style={styles.container}>
<FlatList
style={{width: '100%'}}
data={this.props.data}
renderItem={this.renderRow.bind(this)}
ListFooterComponent={this.renderButton.bind(this)}
/>
</View>
);
}
}
mapStateToProps = state => {
return {
isFetching: state.data.isFetching,
data: state.data.data
}
}
export default connect(mapStateToProps, {fetchData})(App)
Hey change your render method return like below :
return (
{ this.state.isFetching && <View> <Text>Loading</Text> </View>}
<View style={styles.container}>
<FlatList
style={{width: '100%'}}
data={this.props.data}
renderItem={this.renderRow.bind(this)}
ListFooterComponent={this.renderButton.bind(this)}
/>
</View>
);

React Navigation Show/Hide Drawer Items

React Native using React Navigation - Show/Hide Drawer Item
I was wondering if you guys or maybe someone have come up of a better Idea of showing or hiding some menu or Drawer Item under DrawerNavigator.
Basically I have user roles and I want to show/hide selected menu's based on user's role.
My setup now is that I have A DrawerNavigator nested within a StackNavigator.
Menu That Contains My Drawer Items
Hide some drawer items based on user role
Currently Using:
react version 16.0.0-alpha.12
react-native version 0.46.0
react-navigation version 1.0.0-beta.11
You can create your own custom component that will be rendering drawer items
contentComponent: CustomDrawerContentComponent
inside that component you can define logic on show hide your items
example:
const CustomItems = ({
navigation: { state, navigate },
items,
activeItemKey,
activeTintColor,
activeBackgroundColor,
inactiveTintColor,
inactiveBackgroundColor,
getLabel,
renderIcon,
onItemPress,
style,
labelStyle,
}: Props) => (
<View style={[styles.container, style]}>
{items.map((route: NavigationRoute, index: number) => {
const focused = activeItemKey === route.key;
const color = focused ? activeTintColor : inactiveTintColor;
const backgroundColor = focused
? activeBackgroundColor
: inactiveBackgroundColor;
const scene = { route, index, focused, tintColor: color };
const icon = renderIcon(scene);
const label = getLabel(scene);
return (
<TouchableOpacity
key={route.key}
onPress={() => {
console.log('pressed')
onItemPress({ route, focused });
}}
delayPressIn={0}
>
<View style={[styles.item, { backgroundColor }]}>
{icon ? (
<View style={[styles.icon, focused ? null : styles.inactiveIcon]}>
{icon}
</View>
) : null}
{typeof label === 'string' ? (
<Text style={[styles.label, { color }, labelStyle]}>{label}</Text>
) : (
label
)}
</View>
</TouchableOpacity>
);
})}
</View>
);
So in the code above you can define which route will be shown, for instance you can have store with list of items show or hide and from here you can make comparison.
Hope it helps

Resources