How to add a different item to an array based on the key of an array? - react-native

I have this array that fetched from backend !
this is the array.
resultes
[
[front] [18:52:18] Object {
[front] [18:52:18] "cate_id": 1,
[front] [18:52:18] "name": "Vehicles",
[front] [18:52:18] },
[front] [18:52:18] Object {
[front] [18:52:18] "cate_id": 2,
[front] [18:52:18] "name": "Home",
[front] [18:52:18] },
[front] [18:52:18] Object {
[front] [18:52:18] "cate_id": 3,
[front] [18:52:18] "name": "Electronics",
},]
{this.state.categories.results.map((item ,key)=>(
<View key={key}>
<Text>{item.name}</Text>
</View> ))
How can add for every item.name a different icon based on the key of the array?
i tried to make an object
const CATEGORIES_TYPES = {
car: {
style: { width: 50, fontSize: 40, height: 60, color: '#f56217' }
},
home: {
style: { width: 50, fontSize: 40, height: 60, color: '#2c3e50' }
},
tv: {
style: { width: 50, fontSize: 40, height: 60, color: 'black' }
}
};
and in render:
{this.state.categories.results.map((item ,key)=>(
<View key={key}>
{CATEGORIES_TYPES[item.name] && (
<FontAwesome name={CATEGORIES_TYPES[item.name]} style={CATEGORIES_TYPES[item.name].style} />
)}
<Text>{item.name}</Text>
</View> ))
but that don't work Is this a method to add based on the key???

Use conditional rendering or have an object with the names of the icons and the name of category as their key.
const Icons = {
Home: 'home',
Electronics: 'microchip',
Vehicles: 'car',
};
Then in your render do
{this.state.categories.results.map((item, key) => (
<View key={key}>
{CATEGORIES_TYPES[item.name] && (
<FontAwesome
name={Icons[item.name]}
style={CATEGORIES_TYPES[item.name].style}
/>
)}
<Text>{item.name}</Text>
</View>
))}
also change your CATEGORIES_TYPES to look like the keys in your categories object.
const CATEGORIES_TYPES = {
Vehicles: {
style: { width: 50, fontSize: 40, height: 60, color: '#f56217' },
},
Home: {
style: { width: 50, fontSize: 40, height: 60, color: '#2c3e50' },
},
Electronics: {
style: { width: 50, fontSize: 40, height: 60, color: 'black' },
},
};
DEMO

I think this is what you want to do.
const styles = StyleSheet.create({
car: {
width: 50,
fontSize: 40,
height: 60,
color: '#f56217'
},
home: {
width: 50,
fontSize: 40,
height: 60,
color: '#2c3e50'
},
tv: {
width: 50,
fontSize: 40,
height: 60,
color: 'black'
}
});
{this.state.categories.results.map((item ,key)=>(
<View key={key}>
<FontAwesome name={item.name} style={item.name === "car" ? styles.car : item.name === "home" ? styles.home : styles.tv} />
<Text>{item.name}</Text>
</View> ))

Related

how to use TapGestureHandler on Class Components

I'm trying to add a tap gesture to the render card of react-native-deck-swiper using the TapGestureHanlder from react-native-reanimated.
Currently, it works when tapping the red zone outside the swiper, but I want to tap the picture and get the x position where I tapped.
This is the code of the red zone, there I'm calling a class component in which I'm using the deck-swiper with some extra functions.
const onSingleTapEvent = (event) => {
if (event.nativeEvent.state === State.ACTIVE) {
alert('Hey single tap!');
}
};
return (
<GestureHandlerRootView style={{ zIndex: 10 }}>
<TapGestureHandler onHandlerStateChange={onSingleTapEvent}>
<AnimatedView style={styles.container}>
<ImageSwiperDeck
index={index}
listOfAssetsWithinTheAlbum={listOfAssetsWithinTheAlbum}
moveImageToTrashBin={moveImageToTrashBin}
keepImageInAlbum={keepImageInAlbum}
/>
</AnimatedView>
</TapGestureHandler>
</GestureHandlerRootView>
);
};
const styles = StyleSheet.create({
container: {
height: 560,
width: 500,
zIndex: 10,
backgroundColor: 'red',
},
});
export default ImageSwiper;
This is the deck-swiper code which works fine.
return (
<Swiper
ref={(swiper) => {
this.swiper = swiper;
}}
cards={this.props.listOfAssetsWithinTheAlbum}
cardIndex={this.props.index}
renderCard={this.Card}
backgroundColor={'transparent'}
onSwipedLeft={this.deleteImage}
onSwipedRight={this.keepImage}
cardVerticalMargin={10}
stackSize={5}
stackScale={20}
stackSeparation={5}
animateOverlayLabelsOpacity
animateCardOpacity
disableTopSwipe
disableBottomSwipe
overlayLabels={{
left: {
title: 'DELETE',
style: {
label: {
backgroundColor: colors.red,
borderColor: colors.red,
color: colors.white,
borderWidth: 1,
fontSize: 24,
},
wrapper: {
flexDirection: 'column',
alignItems: 'flex-end',
justifyContent: 'flex-start',
marginTop: 20,
marginLeft: -20,
},
},
},
right: {
title: 'KEEP',
style: {
label: {
backgroundColor: colors.blue,
borderColor: colors.blue,
color: colors.white,
borderWidth: 1,
fontSize: 24,
},
wrapper: {
flexDirection: 'column',
alignItems: 'flex-start',
justifyContent: 'flex-start',
marginTop: 20,
marginLeft: 20,
},
},
},
}}
/>
);

How to change the color of the dropdown icon on the right of SectionedMultiSelect - react-native-sectioned-multi-select

I want to change the color of the dropdown icon on the right of SectionedMultiSelect from the default color. Here is my code:
<SectionedMultiSelect
items={items}
IconRenderer={Icon}
uniqueKey="id"
// subKey="children"
selectText="Click to select Expertise"
showDropDowns={true}
onSelectedItemsChange={(selectedItems) => {
setDidUpdate(true);
setExpertise(selectedItems)
}}
selectedItems={expertise}
colors= {{
primary: 'red'
}}
styles={{
// chipText: {
// maxWidth: Dimensions.get('screen').width - 90,
// },
chipContainer : {
// backgroundColor: 'red',
// color: 'green',
borderRadius: 16,
borderWidth: 2,
borderColor: 'white',
marginTop: 10
},
chipText: {
color: 'white',
},
chipIcon: {
color: 'white',
},
button: {
backgroundColor: '#FF9017',
},
selectToggle: {
color: 'white',
},
selectToggleText: {
color: 'white',
// backgroundColor: 'red',
borderWidth: 2,
borderRadius:25,
borderColor: '#fff',
padding: 5,
paddingLeft: 15
},
}}
/>
I have tried changing it through the styles and the color props but to no avail.

Flatlist Multiple item Selection not working on React Native

I am creating a multi-select grid Flatlist and it seems to work fine in IOS phone but it doesn't work at all on Android phone. On IOS it can select an item when clicked, but on Android it just flickers when you select/click an item.
I am really, really can't figure it out. Please help.
Thank you,
[Flatlist-Grid as per code][1]
import {
View, TouchableOpacity, StyleSheet, Text, ScrollView,
Image,
} from 'react-native'
import { MaterialIcons } from '#expo/vector-icons';
export default class StoreSelector extends Component {
constructor() {
super()
this.state = {
dataList: [
{ name: 'exp: date', key: '#ffsdsf', image: <Image style={{ height: 110, width: 110, backgroundColor: 'red' }} /> },
{ name: 'exp: date', key: '#01713f', image: <Image style={{ height: 110, width: 110, backgroundColor: 'red' }} /> },
{ name: 'exp: date', key: '#fsdaff', image: <Image style={{ height: 110, width: 110, backgroundColor: 'red' }} /> },
{ name: 'exp: date', key: '#00b0a6', image: <Image style={{ height: 110, width: 110, backgroundColor: 'red' }} /> },
{ name: 'exp: date', key: '#ffgadsf', image: <Image style={{ height: 110, width: 110, backgroundColor: 'red' }} /> },
{ name: 'exp: date', key: '#fdfdf', image: <Image style={{ height: 110, width: 110, backgroundColor: 'red' }} /> },
{ name: 'exp: date', key: '#fsdff', image: <Image style={{ height: 110, width: 110, backgroundColor: 'red' }} /> },
{ name: 'exp: date', key: '#ec008c', image: <Image style={{ height: 110, width: 110, backgroundColor: 'red' }} /> },
{ name: 'exp: date', key: '#005baa', image: <Image style={{ height: 110, width: 110, backgroundColor: 'red' }} /> },
{ name: 'exp: date', key: '#fceff', image: <Image style={{ height: 110, width: 110, backgroundColor: 'red' }} /> },
{ name: 'exp: date', key: '#ffwf', image: <Image style={{ height: 110, width: 110, backgroundColor: 'red' }} /> },
{ name: 'exp: date', key: '#000', image: <Image style={{ height: 110, width: 110, backgroundColor: 'red' }} /> },
{ name: 'exp: date', key: '#ea3440', image: <Image style={{ height: 110, width: 110, backgroundColor: 'red' }} /> },
]
}
}
componentDidMount() {
let arr = this.state.dataList.map((item, index) => {
this.isSelected = false
return { ...item }
})
this.setState({ dataList: arr })
}
selectionHandler = (ind) => {
const { dataList } = this.state
let arr = dataList.map((item, index) => {
if (ind == index) {
item.isSelected = !item.isSelected
}
return { ...item }
})
this.setState({ dataList: arr })
}
render() {
const { dataList } = this.state
return (
<View style={styles.scrollContainer}>
<ScrollView >
<View style={styles.container}>
{
dataList.map((item, index) => {
return (
<TouchableOpacity
key={item.key}
onPress={() => this.selectionHandler(index)}
style={styles.boxContainer}>
<View style={styles.img}>{item.image}</View>
<View>{item.isSelected ? <MaterialIcons name="check-box" size={24} color="#fbbe2f" /> : <MaterialIcons name="check-box-outline-blank" size={24} color="grey" />}</View>
</TouchableOpacity>
)
})
}
</View>
</ScrollView>
</View>
)
}
}
const styles = StyleSheet.create({
scrollContainer: {
flex: 1,
},
container: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'center',
},
boxContainer: {
height: 110,
width: 110,
margin: 7,
},
img: {
elevation: 5,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
shadowColor: '#000',
shadowOffset: { width: 1, height: 2 },
shadowOpacity: .3,
shadowRadius: 3,
borderWidth: 0.5,
borderColor: '#eee'
}
})
[1]: https://i.stack.imgur.com/hrRVS.png
You have given elevation to image element which is appearing above your checkbox so on select action it gets selected but not visible as it is under the image.
Now question is how will you see the checkbox?
Option 1: Remove the elevation
img: {
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
shadowColor: '#000',
shadowOffset: { width: 1, height: 2 },
shadowOpacity: .3,
shadowRadius: 3,
borderWidth: 0.5,
borderColor: '#eee'
}
Option 2: Make the elevation of the icons higher then the image elevation
<View>
{item.isSelected ?
<MaterialIcons name="check-box" size={24} color="#fbbe2f" style={{elevation:10}}/>
:
<MaterialIcons name="check-box-outline-blank" size={24} color="grey" style={{elevation:10}}/>}
</View>

Type Error : Method.bind is not a function

I am new to React Native. I try to create pie chart using 'react-native-pathjs-charts' library, but I got an error mentioning method.bind is not a function.
Here is my code:
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, Animated, Easing, TouchableOpacity, Image, Linking} from 'react-native';
import { Bar } from 'react-native-pathjs-charts'
import { Pie } from 'react-native-pathjs-charts'
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
android:
'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
type Props = {
};
export default class App extends Component<Props> {
constructor(){
super()
this.state = {
Default_Rating: 2,
Max_Rating : 5,
}
this.Star = 'https://aboutreact.com/wp-content/uploads/2018/08/star_filled.png';
this.Star_With_Border = 'https://aboutreact.com/wp-content/uploads/2018/08/star_corner.png';
this.animatedValue = new Animated.Value(0)
}
UpdateRating( key )
{
this.setState({ Default_Rating: key });
}
componentDidMount(){
this.animate()
}
animate(){
this.animatedValue.setValue(0)
Animated.timing(
this.animatedValue,
{
toValue: 1,
duration: 2000,
easing: Easing.linear
}
).start(() => this.animate())
}
render() {
let data = [{
"name": "Washington",
"population": 7694980
}, {
"name": "Oregon",
"population": 2584160
}, {
"name": "Minnesota",
"population": 6590667,
"color": {'r':223,'g':154,'b':20}
}, {
"name": "Alaska",
"population": 7284698
}]
let options = {
margin: {
top: 20,
left: 20,
right: 20,
bottom: 20
},
width: 350,
height: 350,
color: '#2980B9',
r: 50,
R: 150,
legendPosition: 'topLeft',
animate: {
type: 'oneByOne',
duration: 200,
fillTransition: 3
},
label: {
fontFamily: 'Arial',
fontSize: 8,
fontWeight: true,
color: '#ECF0F1'
}
}
let React_Native_Rating_Bar = [];
for( var i = 1; i <= this.state.Max_Rating; i++ )
{
React_Native_Rating_Bar.push(
<TouchableOpacity
activeOpacity = { 0.7 }
key = { i }
onPress = { this.UpdateRating.bind( this, i ) }>
<Image
style = { styles.StarImage }
source = { ( i <= this.state.Default_Rating ) ? { uri: this.Star } : { uri: this.Star_With_Border } } />
</TouchableOpacity>
);
}
const marginLeft = this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 150]
})
return (
<View style={styles.container}>
<View>
<Text style={styles.title}>Merchant Dashboard</Text>
</View>
<View style = { styles.childView }>
{
React_Native_Rating_Bar
}
</View>
<Text style = { styles.textStyle1 }>
Rating for your shop :
{ this.state.Default_Rating } / { this.state.Max_Rating }
</Text>
<View style={styles.box}>
<Animated.Text
style={{
marginLeft,
color: 'green'}} >
Possitive Reviews!
</Animated.Text>
<Animated.Text
style={{
marginLeft,
color: 'red'
}}>
Negative Reviews!
</Animated.Text>
<View style={styles.btnStyle}>
<TouchableOpacity style={styles.FacebookStyle} activeOpacity={0.5}>
<Image
source={{
uri:
'https://image.flaticon.com/icons/png/512/8/8816.png',
}}
style={styles.ImageIconStyle}
/>
</TouchableOpacity>
<TouchableOpacity style={styles.FacebookStyle} activeOpacity={0.5}>
<Image
source={{
uri:
'https://cdn3.iconfinder.com/data/icons/google-material-design-icons/48/ic_play_circle_outline_48px-512.png',
}}
style={styles.ImageIconStyle}
/>
</TouchableOpacity>
<TouchableOpacity style={styles.FacebookStyle} activeOpacity={0.5}>
<Image
source={{
uri:
'https://image.flaticon.com/icons/png/512/56/56616.png',
}}
style={styles.ImageIconStyle}
/>
</TouchableOpacity>
</View>
</View>
<View style={styles.container1}>
<Pie data={data}
options={options}
accessorKey="population"
margin={{top: 20, left: 20, right: 20, bottom: 20}}
color="#2980B9"
pallete={
[
{'r':25,'g':99,'b':201},
{'r':24,'g':175,'b':35},
{'r':190,'g':31,'b':69},
{'r':100,'g':36,'b':199},
{'r':214,'g':207,'b':32},
{'r':198,'g':84,'b':45}
]
}
r={50}
R={150}
legendPosition="topLeft"
label={{
fontFamily: 'Arial',
fontSize: 8,
fontWeight: true,
color: '#ECF0F1'
}}
/>
</View>
<View>
<Text style={styles.LinkStyle} onPress={ ()=> Linking.openURL('https://google.com') } >Click Here To view Suggetions for improvement of the shop.</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
container1: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f7f7f7',
},
childView:{
justifyContent: 'center',
flexDirection: 'row',
},
StarImage:{
width: 40,
height: 40,
resizeMode: 'cover'
},
textStyle1:
{
textAlign: 'center',
color: '#000',
marginTop: 15
},
title:{
fontSize: 23,
color: '#000'
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
box: {
marginTop: 10,
height:80,
width: 300,
backgroundColor: '#b2beb5',
},
btnStyle:{
justifyContent: 'center',
flexDirection: 'row',
},
btn2Style:{
alignItems: 'flex-end'
},
ImageIconStyle: {
padding: 10,
margin: 5,
height: 25,
width: 25,
resizeMode: 'stretch',
},
TextStyle: {
color: '#fff',
marginBottom: 4,
marginRight: 20,
},
SeparatorLine: {
backgroundColor: '#fff',
width: 1,
height: 40,
},
LinkStyle: {
color: '#E91E63',
textDecorationLine: 'underline'
},
});
In my case the error was due to react-native-d3multiline-chart which occured after upgrading react-native version from 0.54.6 to 0.59.1 because internally react-native-d3multiline-chart was using react-native-svg.
I solved this error by forking this repository and changing the react-native-svg version to 9.3.3 in package.json of react-native-d3multiline-chart.
Pointing to this updated commit id in my project package.json for react-native-d3multiline-chart solved this error for me.
I think you need to use arrow function. Like this,
onPress={() => this.UpdateRating.bind(this, i)}
If this not working, pls share your error log to understand error clearly.
I'm running into this exact issue (after a RN upgrade, 0.57.8 -> 0.59.1) and am almost certain is it because the react-native-pathjs-charts library is no longer maintained, and is pointing at an old / wrong version of react-native-svg.
While I haven't solved it yet (will update), my suspicion is that updating react-native-svg & updating the package.json of react-native-pathjs-charts to not explicitly use v~5.5.1 will get us in the right direction.
UPDATE: I was able to fix it (temporarily, will need to fork the charts repo) by manually updating the module's package.json to point at my own local react-native-svg module:
"dependencies": {
"lodash": "^4.12.0",
"paths-js": "^0.4.5"
},
"peerDependencies": {
"react-native-svg": "9.3.3"
},
Just make sure you're properly adding react-native-svg to your project.

onPanResponderRelease not being triggered

I am trying to use the PanResponder on a View. The onStartShouldSetPanResponder and onMoveShouldSetPanResponder but onPanResponderMove, onPanResponderGrant and onPanResponderRelease does not get triggered at all. My react and react native versions are:
"react": "^15.2.1",
"react-native": "^0.30.0",
Below is the code
'use strict'
import React from 'react'
const Icon = require('react-native-vector-icons/Ionicons')
let THUMB_URLS = require('../Statics/ListingsData.js')
let SidePanelComponent = require('./common/SidePanel.js')
let RecentSearches = require('./Views/RecentSearches/RecentSearches.js')
let TimerMixin = require('react-timer-mixin')
const Loader = require('./common/LoadingState.js')
import { getImageURL, getUserImageURL } from './_helpers/images'
const config = require('../config')
import GoogleAnalytics from 'react-native-google-analytics-bridge'
GoogleAnalytics.setTrackerId(config.google_analytics_id)
const windowSize = require('Dimensions').get('window')
const deviceWidth = windowSize.width
const deviceHeight = windowSize.height
import {
Image,
Text,
View,
TouchableOpacity,
TouchableWithoutFeedback,
ScrollView,
StyleSheet,
Platform,
Animated,
PanResponder
} from 'react-native'
let LISTINGS = []
const ListingsViewComponent = React.createClass({
mixins: [TimerMixin],
getInitialState: function () {
return {
listings: [],
dataSource: [],
showSearchIcon: false,
showSidePanel: false,
photo: {},
componentloading: true,
showHeartIcon: [],
startX: 0,
startY: 0,
showWishlistMenu: false,
wishlistCurrentY: 0,
showNewWishlistTextInput: false,
currentRowdata: {},
wishlistOptions: [],
showrecentsearches: false,
isDataLoading: true,
scrolling: false,
_listViewDirtyPressEnabled: true,
scrollAnimationEnd: false,
scrollStates: [],
goingtonextview: false,
heroImageContainerHeight: deviceWidth,
searchbar: new Animated.ValueXY()
}
},
_panListingsResponder: {},
componentWillMount: function () {
this._panListingsResponder = PanResponder.create({
onStartShouldSetPanResponder: (e, g) => {
this.setState({
startX: e.nativeEvent.pageX,
startY: e.nativeEvent.pageY
})
},
onStartShouldSetPanResponderCapture: (e, g) => {
},
onMoveShouldSetPanResponder: (e, g) => {
this.setState({
heroImageContainerHeight: deviceWidth - (e.nativeEvent.pageY - this.state.startY)
})
},
onMoveShouldSetPanResponderCapture: (e, g) => {},
onPanResponderGrant: (e, g) => {},
onPanResponderMove: (e, g) => {
this.setState({
heroImageContainerHeight: deviceWidth - (e.nativeEvent.pageY - this.state.startY)
})
},
onPanResponderTerminationRequest: (e, g) => {
console.log('onPanResponderTerminationRequest', e.nativeEvent)
return false
},
onPanResponderRelease: (e, g) => {
console.log('_onResponderRelease', e.nativeEvent)
},
onPanResponderTerminate: (e, g) => {
console.log('onPanResponderTerminate', e.nativeEvent)
},
onShouldBlockNativeResponder: (e, g) => true
})
let listingsendpoint = 'http://faithstay-staging.herokuapp.com/api/listings'
this.setState({
isDataLoading: true
})
fetch(listingsendpoint)
.then((response) => response.json())
.then((listingsData) => {
const listings = listingsData
LISTINGS = []
LISTINGS.push(THUMB_URLS[0])
LISTINGS.push(THUMB_URLS[1])
LISTINGS.push(THUMB_URLS[2])
listings.map((listing) => {
LISTINGS.push(listing)
})
this.setState({
isDataLoading: false,
listings: LISTINGS
})
})
.catch((error) => {
console.warn(error)
})
},
componentDidMount: function () {
GoogleAnalytics.trackScreenView('Faithstay-Listings-Page')
},
_showSidePanel: function () {
this.setState({
showSidePanel: true
})
},
_closeSidePanel: function () {
this.setState({
showSidePanel: false
})
},
_showRecentSearches: function () {
this.setState({
showrecentsearches: true
})
},
_closeRecentSearches: function () {
this.setState({
showrecentsearches: false
})
},
componentWillReceiveProps: function () {
this.setState({
goingtonextview: false
})
},
getSearchBarStyle: function () {
return [
styles.searchbar, {
top: this.state.heroImageContainerHeight
}
]
},
render: function () {
let sidePanelViewContainer
if (this.state.showSidePanel) {
sidePanelViewContainer = (<SidePanelComponent {...this.props} imageuri={this.state.photo} onClose={this._closeSidePanel} />)
}
let searchIconContainer = <Animated.View style={this.getSearchBarStyle()}>
<TouchableOpacity style={styles.searchBarInner} onPress={this._showRecentSearches}>
<Text style={styles.searchtext}>
{'Where do you want to go?'}
</Text>
<Icon
name={'ios-search'}
size={30}
color={'#cfcfcf'}
style={styles.searchicon}
/>
</TouchableOpacity>
</Animated.View>
if (!this.state.showrecentsearches) {
if (this.state.isDataLoading) {
return (<Loader />)
} else {
return (
<View style={styles.container} {...this._panListingsResponder.panHandlers}>
<View style={[styles.heroImageContainer, { height: this.state.heroImageContainerHeight }]}>
<Image source={{uri: 'https://faithstay-statics.imgix.net/images/homepage_carousel_4.jpg'}} style={[styles.heroImage, { height: this.state.heroImageContainerHeight }]} />
<View style={[styles.scrimLayer, { height: this.state.heroImageContainerHeight }]} />
<View style={styles.logoContainer}>
<Image source={require('../Statics/images/anchor_3x.png')} style={styles.logoImage} />
<Text style={styles.logoText}>{'FaithStay'}</Text>
</View>
<View style={styles.horizontalDivider} />
<View style={styles.betaVersionContainer}>
<Text style={styles.betaVersionText}>{'Beta Version'}</Text>
</View>
<View style={[styles.pageTitleContainer, {top: this.state.heroImageContainerHeight - 85}]}>
<Text style={styles.pageTitle}>{'Home'}</Text>
</View>
<View style={[styles.movableScrim, {backgroundColor: `rgba(0, 0, 0, ${(deviceWidth - this.state.heroImageContainerHeight) / deviceWidth})`}]} />
</View>
{searchIconContainer}
<ScrollView style={styles.listView}>
{this.getListingsView()}
</ScrollView>
{sidePanelViewContainer}
</View>
)
}
}
return (<RecentSearches {...this.props} closeRecentSearches={this._closeRecentSearches} />)
},
_gotoUserProfilePage: function (user) {
this.props.navigator.push({
id: 15,
passProps: {
user
}
})
},
getListingsView: function () {
let listings = this.state.listings
const listingsArray = []
listings.map((listing, i) => {
let currentlisting = listing
let type = currentlisting.type
if (type !== 'NOT_A_LISTING') {
let imgSource = {
uri: getImageURL(currentlisting.images[0])
}
let profileimg = {
uri: getUserImageURL(currentlisting.host)
}
let title = currentlisting.title
let reviews = '18'
let address_values = currentlisting.google_place.formatted_address ? currentlisting.google_place.formatted_address.split(',') : []
let listing_address = {}
if (address_values.length > 0) {
listing_address = {
country: address_values[address_values.length - 1].trim(),
state: address_values[address_values.length - 2].trim(),
city: address_values[address_values.length - 3].trim()
}
}
let city = listing_address.city + ', ' + listing_address.state
let baseprice = currentlisting.base_price ? '$' + currentlisting.base_price : '0'
listingsArray.push(<View>
<TouchableWithoutFeedback onPress={() => this._pressRow(currentlisting)}>
<View>
<View style={styles.row}>
<Image style={styles.thumb} source={imgSource} >
<View style={styles.priceconatiner}>
<Text style={styles.pricetext}>{baseprice}</Text>
</View>
</Image>
</View>
<TouchableOpacity style={styles.profileImgContainer} onPress={() => this._gotoUserProfilePage(currentlisting.host)}>
<Image style={styles.profileimg} source={profileimg} />
</TouchableOpacity>
<View style={styles.listingtextcontainer}>
<Text style={styles.listingtexttitle}>{title}</Text>
<Text style={styles.listingtexttdescription}>{'Entire Home' + ' - ' + reviews + ' Reviews' + ' - ' + city}</Text>
</View>
</View>
</TouchableWithoutFeedback>
</View>)
} else {
let listing_title = listing.title
let listing_description = listing.description
let imageuri = listing.image;
listingsArray.push(<View><TouchableWithoutFeedback onPress={() => this._pressNonListingRow(currentlisting)}>
<View>
<View style={styles.rowNotListing}>
<Image style={styles.thumbNotListing} source={{uri: imageuri}}>
<View style={styles.thumbNotListing, {position: 'absolute', left:0, top: 0, right:0, bottom:0, backgroundColor: 'rgba(0,0,0,0.2)'}} >
</View>
<View style={styles.thumbNotListingSubContainer}>
<Text style={styles.listingtitle_notlisting}>{listing_title}</Text>
<Text style={styles.listingdescription_notlisting}>{listing_description}</Text>
</View>
</Image>
</View>
</View>
</TouchableWithoutFeedback>
</View>)
}
})
return listingsArray
},
_pressRow: function (listing) {
this.props.navigator.push({
id: 4,
passProps: {
listingdata: listing
}
})
},
_pressNonListingRow: function (listing) {
this.props.navigator.push({
id: 9,
passProps: {
filterData: listing
}
})
}
})
const paddingHorizontal = 15
const paddingVertical = 10
const distanceBetweenIcons = (deviceWidth - 115) / 3
const statusBarHeight = (Platform.OS === 'ios') ? 20 : 0
const isAndroid = Platform.OS === 'android'
const styles = StyleSheet.create({
listView: {
height: deviceHeight - 70,
top: (Platform.OS === 'ios') ? 40 : 0,
left: 0
},
scrimLayer: {
position: 'absolute',
top: 0,
left: 0,
width: deviceWidth,
height: deviceWidth,
backgroundColor: 'rgba(0, 0, 0, 0.2)'
},
movableScrim: {
position: 'absolute',
top: 0,
left: 0,
width: deviceWidth,
height: deviceWidth
},
container: {
flex: 1,
paddingTop: statusBarHeight,
width: deviceWidth,
height: deviceHeight
},
row: {
flexDirection: 'row',
justifyContent: 'center',
backgroundColor: '#f5f5f5',
width: deviceWidth,
height: deviceHeight / 2
},
separator: {
height: 1,
backgroundColor: '#CCCCCC'
},
thumb: {
width: deviceWidth,
height: deviceHeight / 2 - 80
},
thumbNotListing: {
width: deviceWidth,
height: deviceHeight / 2,
justifyContent: 'center'
},
thumbNotListingSubContainer: {
alignSelf: 'center',
justifyContent: 'center'
},
listingtitle_notlisting: {
textAlign: 'center',
alignSelf: 'center',
fontSize: 24,
fontWeight: 'bold',
color: '#ffffff'
},
listingdescription_notlisting: {
textAlign: 'center',
alignSelf: 'center',
fontSize: 16,
marginTop: 10,
color: '#ffffff'
},
text: {
flex: 1,
},
tabbar: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
width: deviceWidth,
height: 49,
backgroundColor: '#f5f5f5',
justifyContent: 'space-between',
borderTopWidth: 1,
borderTopColor: '#dce0e0'
},
searchbar: {
width: deviceWidth - 30,
height: 50,
left: 15,
top: deviceWidth - 5,
position: 'absolute',
justifyContent: 'center',
backgroundColor: '#f5f5f5',
shadowOpacity: 0.5
},
searchBarInner: {
width: deviceWidth - 30,
height: 50,
justifyContent: 'center',
backgroundColor: '#f5f5f5',
shadowOpacity: 0.5
},
searchonlyicon: {
width: 50,
height: 50,
borderRadius: 25,
left: 20,
top: 40,
position: 'absolute',
justifyContent: 'center',
backgroundColor: '#f5f5f5',
shadowOpacity: 0.5
},
searchtext: {
width: 160,
position: 'absolute',
fontSize: 15,
color: '#565a5c',
left: (deviceWidth - 30) / 2 - 80,
top: 15,
fontFamily: 'RobotoCondensed-Regular'
},
searchicon: {
width: 30,
height: 30,
position: 'absolute',
top: 8,
left: 12
},
homeicon: {
width: 30,
height: 30,
position: 'absolute',
top: paddingVertical - 2,
left: paddingHorizontal,
justifyContent: 'center',
},
hearticon: {
width: 40,
height: 30,
position: 'absolute',
top: paddingVertical,
justifyContent: 'center',
left: distanceBetweenIcons
},
emailicon: {
width: 45,
height: 30,
position: 'absolute',
top: paddingVertical,
justifyContent: 'center',
left: 2 * distanceBetweenIcons
},
bagicon: {
width: 35,
height: 20,
position: 'absolute',
top: paddingVertical + 6,
justifyContent: 'center',
left: 3 * distanceBetweenIcons
},
personicon: {
width: 30,
height: 30,
position: 'absolute',
top: paddingVertical,
justifyContent: 'center',
right: paddingHorizontal
},
priceconatiner: {
position: 'absolute',
top: deviceHeight / 2 - 150,
left: 0,
width: 60,
height: 40,
backgroundColor: 'rgba(60,63,64,0.9)',
justifyContent: 'center'
},
pricetext: {
fontSize: 20,
color: '#fff',
fontWeight: 'bold',
textAlign: 'center',
width: 60,
fontFamily: 'HelveticaNeue'
},
profileImgContainer: {
position: 'absolute',
top: deviceHeight / 2 - 108,
right: isAndroid ? 0 : 20, // NOTE: add to width, vs pushing it with position values
width: isAndroid ? 70 : 50, // NOTE: on android, the view must be as big as the image, otherwise the image will be cut off
height: 50,
paddingLeft: paddingHorizontal,
justifyContent: 'center'
},
profileimg: {
width: 50,
height: 50,
borderRadius: 25
},
listingtextcontainer: {
position: 'absolute',
top: deviceHeight / 2 - 70,
left: paddingHorizontal,
justifyContent: 'space-between',
height: 50
},
listingtexttitle: {
paddingTop: 5,
fontSize: 16,
fontFamily: 'HelveticaNeue',
color: '#565a5c',
fontWeight: 'bold'
},
listingtexttdescription: {
fontSize: 14,
fontFamily: 'HelveticaNeue',
color: '#82888a',
paddingBottom: 5
},
wishlistIcon: {
position: 'absolute',
right: 20,
top: 20
},
hearticonwishlist: {
width: 30,
height: 30
},
wishlistScrollView: {
position: 'absolute',
right: 20,
width: deviceWidth - 60,
height: 80,
backgroundColor: '#fff'
},
scrollRow: {
width: 180,
height: 40,
justifyContent: 'center',
padding: 5,
borderBottomWidth: 1,
borderBottomColor: '#f5f5f5'
},
wishlistScrollViewContainer: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
width: deviceWidth,
height: deviceHeight,
backgroundColor: 'rgba(255,255,255,0.1)'
},
touchableScrollViewContainer: {
width: deviceWidth,
height: deviceHeight,
position: 'absolute',
top: 0,
bottom: 0,
left: 0
},
fontWishlistScroller: {
color: '#565a5c',
fontSize: 14
},
rowNotListing: {
flexDirection: 'row',
justifyContent: 'center',
width: deviceWidth,
height: deviceHeight / 2
},
heroImageContainer: {
width: deviceWidth,
height: deviceWidth
},
heroImage: {
width: deviceWidth,
height: deviceWidth
},
logoContainer: {
width: 120,
position: 'absolute',
left: (deviceWidth / 2) - 60,
top: 19,
flexDirection: 'row',
justifyContent: 'center',
backgroundColor: 'transparent'
},
logoImage: {
width: 18,
height: 30,
top: 3
},
logoText: {
fontFamily: 'RobotoCondensed-Regular',
fontSize: 25,
fontWeight: '400',
textAlign: 'center',
color: '#fffff0',
marginLeft: 7.7
},
horizontalDivider: {
width: 32,
position: 'absolute',
left: deviceWidth / 2 - 16,
top: 59,
borderBottomWidth: 1,
borderColor: '#ffffff'
},
betaVersionContainer: {
width: 120,
position: 'absolute',
left: deviceWidth / 2 - 60,
top: 79,
justifyContent: 'center',
backgroundColor: 'transparent'
},
betaVersionText: {
fontFamily: 'RobotoCondensed-Regular',
fontSize: 14,
fontStyle: 'italic',
fontWeight: '300',
textAlign: 'center',
color: '#ffffff',
alignSelf: 'center'
},
pageTitleContainer: {
position: 'absolute',
top: deviceWidth - 85,
left: 20,
backgroundColor: 'transparent'
},
pageTitle: {
fontSize: 34,
fontFamily: 'RobotoCondensed-Bold',
color: '#ffffff'
}
})
module.exports = ListingsViewComponent
I got it working properly by using onPanResponderEnd instead of onPanResponderRelease.
Also if we still want to use onPanResponderRelease then we should allow termination request by:
onPanResponderTerminationRequest: () => true
You need to make sure the following handlers return true
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
The only different between onStart... and onMove... is that the PanResponder will be created when you start rendering the component for onStart..., it will be created (lazy) when user start tab or move for onMove.
On android side, you may still find that onPanResponderRelease will not be triggered, an issue reported here as well https://github.com/facebook/react-native/issues/9447
I ended up using onPanResponderTerminate to handle this case. Hopefully you can get more insights about it.