How can I change the numColumns of Flatlist when changing the orientation - react-native

I created the function name getOrientation() and put it at useEffect so that whenever I rotate the device, it will rerender the component and show me which orientation the device is.
I also created the variable to determine the orientation by using hook
getOrientation()
const [orientation, setOrientation] = useState("")
const window = useWindowDimensions()
const getOrientation = () => {
if (window.height < window.width) {
setOrientation("LANDSCAPE")
} else {
setOrientation("PORTRAIT")
}
return orientation
}
at useEffect
useEffect(() => {
getOrientation()
})
console.log(orientation)
My problem is that I want to set the numsColumns = 2 in Flatlist (LANDSCAPE) and equals to 1 for the portrait mode but the error popup telling me that I cannot change the numColumns on the fly. What should I do ?
Here is my flatlist
<View style={styles.container}>
<FlatList
contentContainerStyle={{
paddingLeft: insets.left,
paddingRight: insets.right,
}}
data={dishes.dishes}
numColumns={orientation == "LANDSCAPE" ? 2 : 1}
renderItem={({ item, index }) => (
<Tile
style={styles.tileItem}
key={index}
title={item.name}
caption={item.description}
onPress={() => navigation.navigate('Dishdetail_Screen', { dishId: item.id })} // passing infor from one to another screen
// chevron={false}
featured
imageSrc={{
uri: baseUrl + item.image
}}
/>
)}
keyExtractor={item => item.id.toString()} />
</View>
This creepy error
enter image description here
P/s: I'm the fresh React-Native developer. Thanks everyone who is checking my problems

Try adding a key prop to your Flatlist like so with the value being ur orientation:
<FlatList
key={orientation} // add key prop here
contentContainerStyle={{
paddingLeft: insets.left,
paddingRight: insets.right,
}}
data={dishes.dishes}
numColumns={orientation == "LANDSCAPE" ? 2 : 1}
renderItem={({ item, index }) => (
<Tile
style={styles.tileItem}
key={index}
title={item.name}
caption={item.description}
onPress={() => navigation.navigate('Dishdetail_Screen', { dishId: item.id })} // passing infor from one to another screen
// chevron={false}
featured
imageSrc={{
uri: baseUrl + item.image
}}
/>
)}
keyExtractor={item => item.id.toString()} />

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>
);
}

FlatList search bar does not persist keyboard React Native

I'm fetching data from an API and implementing search in a FlatList but the keyboard dismisses automatically after every key-press.
I'm refering this article but implementing it in a Functional Component.
const renderHeader = () => {
return <SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={text => searchFilterFunction(text)}
value={value}
autoCorrect={false} />;
}
const searchFilterFunction = (text) => {
setValue(text);
const newData = APIData.filter(item => {
const itemData = `${item.name.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.includes(textData);
});
setData(newData);
}
return (
<FlatList
keyExtractor={(item) => item._id}
data={data}
ItemSeparatorComponent={renderSeparator}
ListHeaderComponent={renderHeader}
ListFooterComponent={renderFooter}
onRefresh={handleRefresh}
refreshing={refreshing}
renderItem={({ item }) => (
<Card>
<Card.Content style={{ flexDirection: "row" }}>
<Text>{"Name: " + item.name}</Text>
<Text>{"Status: " + (item.isaccepted ? "Accepted" : "Pending")}</Text>
<Text>{"ID: " + item.id}</Text>
</Card.Content>
</Card>
)} />
)
Thanks in advance.
I was doing same thing, adding search bar as a header to FlatList. Unfortunately, this also updates the header when you update the flatlist data when search filtering is complete and hence focusing out of SearchBar. At the end, due to time constraints, I ended up putting SearchBar at the top of FlatList.
Try rendering your ListHeaderComponent as JSX element directly, instead of using callback
<FlatList
ListHeaderComponent={
<View>
<Text>I am the header</Text>
</View>
}
...props
/>

FlatList Weird Behavior after appending new data - multiple automatic scrolling

I have a list of data that shows posts feed, whenever user scrolls, the list automatically scrolls multiple times up or down which results in a very bad UX
tried the workaround provided here:
https://stackoverflow.com/questions/43611485/prevent-flatlist-from-scrolling-when-new-items-are-added#=
but it doesn't seem to work for me.
<FlatList
keyboardDismissMode = 'on-drag'
keyboardShouldPersistTaps = 'always'
getItemLayout={(data, index) => (
{length: 575, offset: 575 * index, index}
)}
onScroll = {(event) => {
this.flatListOffset = event.nativeEvent.contentOffset.y
this.setState({
scrollOffset: event.nativeEvent.contentOffset.y
})
}}
contentContainerStyle={{
flexGrow: 1,
width: Dimensions.get('window').width
}}
refreshControl={
<RefreshControl
progressViewOffset={120}
refreshing={this.state.refreshing}
onRefresh={this._onRefresh.bind(this)}
/>
}
ref={(feedView) => (this.feedView = feedView)}
data={this.state.feed}
onEndReached={() => {
if (this.state.hasNextPage && !this.state.loadingMore) {
this.paginateFeed()
}
}}
ListHeaderComponent={this._renderCirclesBar}
ListEmptyComponent={this._renderEmptyState}
ListFooterComponent={() =>
!this.state.loadingMore ? null : (
<ActivityIndicator color="#2962FF" size="large" />
)
}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
then at some point:
this.setState({ feed: this.state.feed.concat(....) })
a GIF explaining the issue:
https://gifyu.com/image/9gDD
User PureComponent for rendering and set prop disableVirtualization={true} in flatlist

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]]}

React Native Flatlist renderItem

Working with React Native, having some issues with the FlatList component.
This is my FlatList
<FlatList
data={this.state._data}
renderItem={() => this.renderItem()}
refreshControl={
<RefreshControl
onRefresh={() => this.handleRefresh}
refreshing={this.state.refreshing}
/>
}
/>
This is my renderItem function:
renderItem({item, index}) {
return (
<View style={{marginTop: 10, marginHorizontal: 10, paddingLeft:
10}}>
<ListItem
roundAvatar
title={`${item.itemName}`}
subtitle={`${item.amount}`}
avatar={require('../../../images/logo.png')}
/>
<View
style={{
paddingBottom: 10,
paddingTop: 10,
display: 'flex',
flexDirection: "row",
justifyContent: "space-around",
alignContent: "center"
}}
>
<View style={{ flexDirection: "row", alignContent:
"center", width:"45%"}}>
<Button
block
small
// disabled={this.state.acceptButtonGray}
style=
{this.state.acceptButtonGray ? ({
backgroundColor: 'gray',
width: "100%"
}) : ({backgroundColor: "#369ecd",
width: "100%"
})}
onPress={() =>
this.setState({
modalVisible: true,
// acceptOrDeclineModalText: `Accept offer for ${item.amount} ${'\b'} Are you Sure?`,
acceptOffer: true,
})
}
>
<Text>
Accept
</Text>
</Button>
</View>
</View>
</View>
);
}
this.setState in the onPress in the button should make a Modal visible, and set acceptOffer to true. Modal opens, user confirms their offer. The offer button which opened that modal now should be gray, and even better, disabled.
Passing my RenderItem function as shown above, I receive
TypeError: Cannot read property 'item' of undefined.
Passing my RenderItem function like this:
renderItem={this.renderItem}
I Get This Error:
_this2.setState is not a function
The FlatList Component is certainly responsible for part of my issue, as well as how and where I am calling this.setState. Only one button is shown in my post, but there are two, one for accept, one for decline. Would having two modals change anything?
The FlatList displays my ListItem components with ease until I attempt to call this.setState in the buttons inside the View which contains those ListItems.
The Modal close button takes this.state.acceptOffer and if true, sets this.state.acceptButtonGray to true, should this logic be somewhere else?
Is there another way to open a modal and change the button color without using component state? Does react want these buttons inside of a TouchableOpacity?
I greatly appreciate any help given.
you should write a renderItem function like this
renderItem = ({item, index}) => {
// return here
}
Change your renderItem method to renderItem={this.renderItem.bind(this)}?
As per my Knowledge item and index are passed as object in flatlist's renderItem
so we can pass by two ways
1 way
Flatlist Component
<FlatList
data={this.state.data}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item, index }) => this._renderItem(item, index)} //Passing as object from here.
/>
Render Item
_renderItem = (item, index) => {
console.log(item)
console.log(index)
}
2 way
Flatlist Component
<FlatList
data={this.state.data}
keyExtractor={(item, index) => index.toString()}
renderItem={( item, index ) => this._renderItem(item, index)}
/>
Render Item
_renderItem = ({item, index}) => { // Passing as object from here
console.log(item)
console.log(index)
}
1) You can write function as -
renderItem = ({item, index}) => {
// return here
}
2) or else if you want to execute your function then -
<FlatList
data={this.state._data}
renderItem={(item) => this.renderItem.bind(this, item)}
refreshControl={
<RefreshControl
onRefresh={() => this.handleRefresh}
refreshing={this.state.refreshing}
/>
}
/>
You have to use bind(this,item) or change function like (item)=>.
I experienced the same issue and wasted many hours to figure out why it was not re-rendering:
We need to set extraData prop of FlatList if there is any change in the state like so:
<FlatList data={this.state.data} extraData={this.state} .../>
Please see the official documentation here:
https://facebook.github.io/react-native/docs/flatlist.html