I would like to display the content "product_id" from
the "products" array
I'm setting up a new component to view the product_id from the
products array.
Here the expectation is to display the contents of the array.
import { View, Text } from 'react-native'
import { Button, SpinnerButton } from '../../components'
import { inject, observer } from 'mobx-react/native'
import styles from './style'
import { calcSize } from '../../utils'
#inject('UserStore')
#observer
class ProductInfo extends Component {
constructor(props) {
super(props)
// this.state = {}
// this.item = this.props.navigation.state.item ? this.props.navigation.state.params.item : 'no item'
}
render() {
return (
<View style={styles.container}>
{this.props.navigation.state.params.item.products.map(pr => (
<Text>{pr.product_id}</Text>
))}
</View>
)
}
}
export default ProductInfo
here i send the param with the "ProductInfo" to the page above..
import { View, Text, FlatList, TouchableOpacity } from 'react-native'
import Style from './style'
import { inject, observer } from 'mobx-react/native'
import Icon from 'react-native-vector-icons/MaterialIcons'
import React, { Component } from 'react'
let mounted = false
#inject('UserStore', 'NavigationStore')
#observer
class DynamicList extends Component {
constructor(props) {
super(props)
this.state = {
data: props.data,
currentSearch: props.currentSearch,
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.data !== this.state.data) {
this.setState({ data: nextProps.data, currentSearch: nextProps.currentSearch })
}
}
_renderSuggestion = ({ item, index }) => {
console.log('_renderSuggestion', item)
const splittedName = item.split(' ')
let splittedSearch = this.state.currentSearch.toUpperCase().split(' ')
splittedSearch = splittedSearch.map(x => x.trim()).filter(x => x.length > 1)
let suggestion = []
if (splittedSearch.length == 0) {
suggestion = splittedName.map((word, index) => <Text key={index}>{word} </Text>)
} else {
let highlightedWords = []
splittedName.forEach((word, index) =>
splittedSearch.forEach(wordFromSearch => {
const currentWord = word.toUpperCase()
const isAlreadyHighlighted = highlightedWords.includes(currentWord)
if ((currentWord.includes(wordFromSearch.toUpperCase()) && this.state.currentSearch.length > 0) || isAlreadyHighlighted) {
let v = (
<Text key={index} style={{ color: '#2eb872' }}>
{word}{' '}
</Text>
)
if (!isAlreadyHighlighted) {
highlightedWords.push(currentWord)
}
suggestion[index] = v
} else {
let v = <Text key={index}>{word} </Text>
suggestion[index] = v
}
})
)
}
return (
<TouchableOpacity
style={Style.suggestionView}
onPress={() => {
this.props.UserStore.addRecentSearch(item)
this.props.NavigationStore.navigate({ routeName: 'ProductInfo', params: { item } })
//this.props.NavigationStore.navigate({ routeName: 'SearchResult', params: { item: item } })
this.autoCompleteTimeout = setTimeout(() => {
if (mounted) this.setState({ showAutoComplete: false })
}, 400)
}}
>
<Icon name='search' size={20} style={{}} />
<Text style={{ marginLeft: 20, textAlign: 'left', color: '#9B999A' }}>{suggestion}</Text>
</TouchableOpacity>
)
}
render() {
console.log('data:', this.state.data)
return (
<View style={{ paddingVertical: 10 }}>
<FlatList initialNumToRender={20} data={this.state.data} keyExtractor={(item, index) => item.toString()} renderItem={this._renderSuggestion} keyboardShouldPersistTaps='always' />
</View>
)
}
}
let Results = props => {
console.log(props)
switch (props.navigation.state.key) {
case 'Products': {
let data = props.screenProps.data.suggestions.products.map(pr => pr.product_title)
return <DynamicList data={data} currentSearch={props.screenProps.currentSearch} />
break
}
case 'Brands': {
let data = props.screenProps.data.suggestions.warehouses.map(pr => pr.warehouse_name)
return <DynamicList data={data} currentSearch={props.screenProps.currentSearch} />
break
}
case 'Categories': {
let data = props.screenProps.data.suggestions.categories.map(pr => pr.categories)
return <DynamicList data={data} currentSearch={props.screenProps.currentSearch} />
break
}
case 'UPC': {
let data = props.screenProps.data.suggestions.upcs.map(pr => pr.product_title)
return <DynamicList data={data} currentSearch={props.screenProps.currentSearch} />
break
}
case 'Tags': {
let data = props.screenProps.data.suggestions.tags.map(pr => pr.product_title)
return <DynamicList data={data} currentSearch={props.screenProps.currentSearch} />
break
}
}
return <Text>Home</Text>
}
const TabNavigator = createMaterialTopTabNavigator(
{
Products: Results,
Brands: Results,
Categories: Results,
UPC: Results,
Tags: Results,
},
{
tabBarOptions: {
style: {
backgroundColor: 'black',
},
labelStyle: {
fontSize: 9,
margin: 0,
padding: 0,
fontFamily: 'Poppins-bold',
},
// etc
},
}
)
let f = Component => {
let r = props => {
// alert(props)
return <Component {...props} />
}
return r
}
export default createAppContainer(TabNavigator)
i want to press on item and it will show me the info of it on "ProductInfo"
You have not {} around the map. You also need to check props.screenProps.suggestions and props.screenProps.suggestions.products for null or undefined.
<View style={styles.container}>
{this.props.screenProps.suggestions.products.map(pr => <Text>{pr.product_id}</Text>)}
</View>
Please comment if you have any error when rendering.
Related
I am seeing very sluggish performance with my following implementation.
Mainly, but not only, when I disable MealCarousel, it gets better, since the Carousel renders images.
Another way I can improve performance is by adding initialNumToRender to the Main Screen FlatList, but I would really love to see if my components themselves could be improved. I do believe I am not writing an optimized code, but not sure how to improve this. Would really appreciate suggestions and explanations!
Main Screen
const renderResults = useCallback(
({ item }) => <RestaurantListRow {...item} />,
[]
);
<FlatList
ref={topListRef}
data={results}
keyExtractor={(item: { id: string }) => `row-${item.id}`}
renderItem={renderResults}
/>
RestaurantListRow
import React, { useState } from 'react';
import { Pressable, StyleSheet } from 'react-native';
import { NativeViewGestureHandler } from 'react-native-gesture-handler';
import MealCarousel, { Pagination } from 'react-native-snap-carousel';
import { View, Divider, Layout, Colors } from 'theme';
import { Restaurant, Meal } from 'types';
import { getCurrentFormattedTime, navigate } from 'utils';
import RestaurantMeal from './RestaurantMeal';
const width = Layout.window.width;
const RestaurantListRow = React.memo(
({
id,
name,
rating,
number_of_ratings,
meals,
address,
phone_number,
coordinates,
open_now,
closes_at,
pickup_hours_today,
curbside_pickup,
opening_hours,
opening_hours_array,
categories,
}) => {
let favorite_restaurant_open_now = false;
if (open_now === undefined) {
const today_from_js = new Date().getDay();
const today = today_from_js - 1 === -1 ? 6 : today_from_js - 1;
const time_now_rounded_down = getCurrentFormattedTime();
favorite_restaurant_open_now = opening_hours_array
? opening_hours_array[today].includes(time_now_rounded_down)
: false;
}
const calculated_open_now = open_now !== undefined ? open_now : favorite_restaurant_open_now;
const renderItem = React.useCallback(
({ index, item }: { index: number; item: Meal }) => {
return (
<Pressable
onPress={() => {
navigate('Restaurant', {
index, // Index for pagination
id,
name,
rating,
number_of_ratings,
meals,
coordinates,
address,
open_now: calculated_open_now,
closes_at,
pickup_hours_today,
curbside_pickup,
opening_hours,
categories,
});
}}
>
<RestaurantMeal meal={item} />
</Pressable>
);
},
[
address,
categories,
coordinates,
curbside_pickup,
id,
meals,
name,
calculated_open_now,
closes_at,
opening_hours,
pickup_hours_today,
rating,
number_of_ratings,
]
);
const [activeSlide, setActiveSlide] = useState<number>(0);
return (
<View>
<NativeViewGestureHandler disallowInterruption>
<MealCarousel
data={meals}
renderItem={renderItem}
sliderWidth={width}
itemWidth={width - 30}
layout="stack"
layoutCardOffset={18}
onSnapToItem={(index) => setActiveSlide(index)}
vertical={false}
/>
</NativeViewGestureHandler>
<Pagination
dotsLength={meals.length}
activeDotIndex={activeSlide}
containerStyle={{ paddingTop: 15, paddingBottom: 0 }}
dotStyle={{
width: 8,
height: 8,
borderRadius: 5,
marginHorizontal: 8,
backgroundColor: Colors.grey_400,
}}
inactiveDotOpacity={0.4}
inactiveDotScale={0.6}
/>
<Divider />
</View>
);
}
// () => true
);
export default RestaurantListRow;
Meal
import { Skeleton } from '#motify/skeleton';
import { cancelPendingOrder, useMutation } from 'dineden-graphql';
import { LinearGradient } from 'expo-linear-gradient';
import { useColorScheme } from 'hooks';
import moment from 'moment';
import React, { useCallback, useState } from 'react';
import { Image, ImageStyle, StyleSheet } from 'react-native';
import { Icon } from 'react-native-elements';
import { Colors, Sizes, View, Text, Button, ColorScheme } from 'theme';
import { Meal, Restaurant } from 'types';
import { useStore, PriceBadge, navigate } from 'utils';
const RestaurantMeal = ({
meal,
restaurant,
additionalMealsAvailable,
showGetThisButton,
showUpcomingOrderExtras,
pickup_time,
showDescription,
skeletonRadius = 'square',
}) => {
const { id, image_url, original_price, price_category, title, description } = meal;
const user = useStore(useCallback((state) => state.user, []));
const setUpcomingMealCanceled = useStore((state) => state.setUpcomingMealCanceled);
const upcomingMeal = useStore((state) => state.upcomingMeal);
const setAlert = useStore(useCallback((state) => state.setAlert, []));
const [buttonLoading, setButtonLoading] = useState<boolean>(false);
const [imageLoaded, setImageLoaded] = useState<boolean>(false);
const [GQL_cancelPendingOrder, { loading, error, data }] = useMutation(cancelPendingOrder);
const colorScheme = useColorScheme();
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 1 }}>
<Skeleton show={!imageLoaded} radius={skeletonRadius}>
<View style={{ height: '100%' }}>
<Image
source={{ uri: image_url }}
onLoadEnd={() => {
setImageLoaded(true);
}}
resizeMode="cover"
/>
</View>
</Skeleton>
<LinearGradient colors={['transparent', 'rgba(0,0,0,0.6)']}>
<View>
<PriceBadge />
</View>
<View>
<Text>{title}</Text>
{additionalMealsAvailable && (
<View>
<Text>
{additionalMealsAvailable} more {additionalMealsAvailable > 1 ? 'meals' : 'meal'}
</Text>
</View>
)}
{showGetThisButton && (
<View>
{upcomingMeal &&
upcomingMeal.meal.id === id &&
upcomingMeal.restaurant?.id === restaurant?.id &&
user ? (
<Button
title="Selected"
size="l"
icon={{
name: 'check-circle',
size: 15,
color: 'white',
containerStyle: {
marginRight: 5,
},
}}
onPress={() => {
navigate('Meals', {
screen: 'Meals',
params: { screen: 'Upcoming' },
});
}}
/>
) : (
<Button
title="Get this meal"
size="l"
onPress={() => {
if (user) {
if (upcomingMeal) {
if (upcomingMeal.pickup_time) {
const time_now = moment();
const pickup_time = moment(upcomingMeal.pickup_time);
if (time_now > pickup_time) {
setAlert({
type: 'pickupExpired',
meal,
restaurant,
});
} else {
setAlert({
type: 'pickupAlreadyExists',
meal,
restaurant,
});
}
} else {
setAlert({
type: 'replaceUpcomingMeal',
height: 180,
meal,
restaurant,
});
}
} else {
setAlert({
type: 'getThisMeal',
height: 180,
meal,
restaurant,
});
}
} else {
setAlert({ type: 'pleaseSignIn' });
}
}}
/>
)}
</View>
)}
</View>
</LinearGradient>
</View>
<Text>{description}</Text>
</View>
);
};
export default RestaurantMeal;
const items=[{
id:1,
title:'one'
},{
id:2,
title:'two'
}]
//...
renderItem = ({ item }) => (<View key={item.key}><Text>{item.title}</Text></View>);
render(){
// ...
<FlatList
key={(id)=>item.id}
data={items}
renderItem={renderItem}
/>
// ...
}
I'm new to react native and i am trying to make a contacts checkmark from FlatList with Large data items.
We can select one or more items and I want to pass these all selected contacts to another screen when i clicked on DONE button ..
like this in the picture
React Code
import React, { Component } from 'react'
import {
View,
Text,
Dimensions,
TouchableOpacity,
FlatList,
ActivityIndicator,
Image,
TextInput,
PermissionsAndroid,
Platform,
} from 'react-native'
import Feather from "react-native-vector-icons/Feather"
import ContactsLib from 'react-native-contacts'
import { styles } from './ContactStyles'
import PropTypes from 'prop-types'
export class Contacts extends Component {
static navigationOptions = ({navigation}) => {
return {
headerTitle: () => (
<View style={{flex: 1, alignSelf: 'center'}}>
<Text style={{
color: '#fff',
alignSelf: 'center',
fontSize: 22,
}}>Contacts</Text>
</View>
),
headerRight: () => (
<TouchableOpacity onPress={() => navigation.goBack(null)}
style={{right: Platform.OS === 'ios' ? Dimensions.get("window").height < 667 ? '10%' : '5%' : '25%', backgroundColor: 'black', paddingLeft: 15}}>
<Text style={{fontSize: 18, color: '#fff'}}>Done</Text>
</TouchableOpacity>
),
headerLeft: () => (
<TouchableOpacity onPress={() => navigation.goBack(null)} style={{left: Dimensions.get("window").height < 667 ? '8%' : '3%', backgroundColor: 'black', width: '100%'}}>
<Feather style = {{paddingLeft : 10}} name="chevron-left" size={26} color="white" />
</TouchableOpacity>
),
headerStyle: {
backgroundColor: 'black',
},
headerTintColor: '#fff',
};
};
constructor(props) {
super(props)
this.state = {
contactList: [],
selectedContact: [],
text: '',
isLoading: true,
}
this.arrayholder = []
}
async componentDidMount() {
if (Platform.OS === 'android') {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_CONTACTS,
{
title: 'App Contact Permission',
message: 'This App needs access to your contacts ',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
}
)
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
this.getListOfContacts()
} else {
this.setState({ isLoading: false })
this.getOtherContacts()
}
} catch (err) {
this.setState({ isLoading: false })
}
} else {
ContactsLib.checkPermission((err, permission) => {
if (permission === 'denied') {
this.setState({ isLoading: false })
this.getOtherContacts()
} else {
this.getListOfContacts()
}
})
}
}
// Mics Method
getOtherContacts = () => {
const { otherContactList } = this.props
const arrFinal = []
if (otherContactList.length > 0) {
otherContactList.map((listItem) => {
arrFinal.push(listItem)
})
}
arrFinal.map((listItem, index) => {
listItem.isSelected = false
listItem.id = index
})
this.setState({ contactList: arrFinal, isLoading: false })
this.arrayholder = arrFinal
}
getListOfContacts = () => {
const { otherContactList } = this.props
const arrFinal = []
ContactsLib.getAll((err, contacts) => {
if (err) {
throw err
}
contacts.map((listItem) => {
arrFinal.push({
fullname: listItem.givenName + ' ' + listItem.familyName,
phoneNumber: listItem.phoneNumbers.length > 0 ? listItem.phoneNumbers[0].number : '',
avatar: listItem.thumbnailPath,
})
})
if (otherContactList.length > 0) {
otherContactList.map((listItem) => {
arrFinal.push(listItem)
})
}
arrFinal.map((listItem, index) => {
listItem.isSelected = false
listItem.id = index
})
this.setState({ contactList: arrFinal, isLoading: false })
this.arrayholder = arrFinal
})
}
getSelectedContacts = () => {
const { selectedContact } = this.state
return selectedContact
}
checkContact = (item) => {
const { onContactSelected, onContactRemove } = this.props
let arrContact = this.state.contactList
let arrSelected = this.state.selectedContact
arrContact.map((listItem) => {
if (listItem.id === item.id) {
listItem.isSelected = !item.isSelected
}
})
if (item.isSelected) {
arrSelected.push(item)
if (onContactSelected) {
onContactSelected(item)
}
} else {
if (onContactRemove) {
onContactRemove(item)
}
arrSelected.splice(arrSelected.indexOf(item), 1)
}
this.setState({ contactList: arrContact, selectedContact: arrSelected })
}
checkExist = (item) => {
const { onContactRemove } = this.props
let arrContact = this.state.contactList
let arrSelected = this.state.selectedContact
arrContact.map((listItem) => {
if (listItem.id === item.id) {
listItem.isSelected = false
}
})
if (onContactRemove) {
onContactRemove(item)
}
arrSelected.splice(arrSelected.indexOf(item), 1)
this.setState({ contactList: arrContact, selectedContact: arrSelected })
}
SearchFilterFunction = (text) => {
let newArr = []
this.arrayholder.map(function(item) {
const itemData = item.fullname.toUpperCase()
const textData = text.toUpperCase()
if (itemData.indexOf(textData) > -1) {
newArr.push(item)
}
})
this.setState({
contactList: newArr,
text: text,
})
}
//Render Method
_renderSeparator = () => {
const { sepratorStyle } = this.props
return <View style={[styles.sepratorStyle, sepratorStyle]} />
}
_renderItem = ({ item }) => {
const { viewCheckMarkStyle } = this.props
return (
<TouchableOpacity onPress={() => this.checkContact(item)}>
<View style={styles.viewContactList}>
<Image
source={item.avatar !== '' ? { uri: item.avatar } : require('./Resources/user.png')}
style={styles.imgContactList}
/>
<View style={styles.nameContainer}>
<Text style={styles.txtContactList}>{item.fullname}</Text>
<Text style={styles.txtPhoneNumber}>{item.phoneNumber}</Text>
</View>
{item.isSelected && (
<Image
source={require('./Resources/check-mark.png')}
style={[styles.viewCheckMarkStyle, viewCheckMarkStyle]}
/>
)}
</View>
</TouchableOpacity>
)
}
_renderItemHorizontal = ({ item }) => {
const { viewCloseStyle } = this.props
return (
<View style={styles.viewSelectedContactList}>
<Image
source={item.avatar !== '' ? { uri: item.avatar } : require('./Resources/user.png')}
style={styles.imgSelected}
/>
<TouchableOpacity
onPress={() => this.checkExist(item)}
style={[styles.viewCloseStyle, viewCloseStyle]}
>
<Image source={require('./Resources/error.png')} style={styles.imgCancelStyle} />
</TouchableOpacity>
<Text style={styles.txtSelectedContact} numberOfLines={1}>
{item.fullname}
</Text>
</View>
)
}
render() {
const { searchBgColor, searchPlaceholder, viewSepratorStyle } = this.props
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<View style={[styles.viewSearch, { backgroundColor: searchBgColor }]}>
<TextInput
style={styles.searchInput}
placeholder={searchPlaceholder}
onChangeText={this.SearchFilterFunction}
/>
</View>
<View>
<FlatList
showsHorizontalScrollIndicator={false}
data={this.state.selectedContact}
extraData={this.state}
renderItem={this._renderItemHorizontal}
horizontal
/>
</View>
{this.state.selectedContact.length > 0 && (
<View style={[styles.viewSepratorStyle, viewSepratorStyle]} />
)}
{this.state.contactList.length > 0 && (
<FlatList
ListFooterComponent={this._renderSeparator}
ItemSeparatorComponent={this._renderSeparator}
showsVerticalScrollIndicator={false}
data={this.state.contactList}
renderItem={this._renderItem}
onEndReachedThreshold={0.3}
extraData={this.state}
keyExtractor={(item) => item.id.toString()}
/>
)}
{this.state.isLoading && (
<View style={styles.loading}>
<ActivityIndicator animating={true} size="large" color="gray" />
</View>
)}
</View>
)
}
}
StyleSheet
Contacts.propTypes = {
otherContactList: PropTypes.array,
viewCloseStyle: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]),
viewCheckMarkStyle: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]),
sepratorStyle: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]),
viewSepratorStyle: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]),
searchBgColor: PropTypes.string,
searchPlaceholder: PropTypes.string,
onContactSelected: PropTypes.func,
onContactRemove: PropTypes.func,
}
Contacts.defaultProps = {
otherContactList: [],
viewCloseStyle: {},
viewCheckMarkStyle: {},
sepratorStyle: {},
viewSepratorStyle: {},
searchBgColor: 'rgb(202,201,207)',
searchPlaceholder: 'Search...',
onContactSelected: () => {},
onContactRemove: () => {},
}
export default Contacts
Please Help me..
Thanks.....
I'm setting up a component that render a flatlist and I'm trying to make the flatlist items "fade in" animation For a more impressive display
This is a component for render search suggestions that show items
import React, { Component } from 'react'
import { View, Text, TextInput, FlatList, Image, TouchableOpacity } from 'react-native'
import { inject, observer } from 'mobx-react/native'
import { Button } from '../../components'
import Style from './style'
import I18n from '../../i18n'
import Icon from 'react-native-vector-icons/MaterialIcons'
import Api from '../../utils/Api'
import _ from 'lodash'
let mounted = false
#inject('UserStore', 'NavigationStore')
#observer
class SearchProducts extends Component {
constructor(props) {
super(props)
this.state = {
searchAutoComplete: [],
showAutoComplete: false,
currentSearch: '',
searchTimeoutId: null,
}
this.autoCompleteTimeout = null
this.storedResults = []
}
componentWillMount() {
mounted = true
}
componentWillUnmount() {
mounted = false
clearTimeout(this.autoCompleteTimeout)
}
_renderCategory = ({ item }) => {
return (
<View style={Style.featuredView}>
<Image source={item.image} style={Style.featuredImage} />
<Text style={{ textAlign: 'center', color: '#9B999A' }}>{item.title}</Text>
</View>
)
}
_renderSuggestion = ({ item, index }) => {
const splittedName = item.split(' ')
let splittedSearch = this.state.currentSearch.toUpperCase().split(' ')
splittedSearch = splittedSearch.map(x => x.trim()).filter(x => x.length > 1)
let suggestion = []
if (splittedSearch.length == 0) {
suggestion = splittedName.map((word, index) => <Text key={index}>{word} </Text>)
} else {
let highlightedWords = []
splittedName.forEach((word, index) =>
splittedSearch.forEach(wordFromSearch => {
const currentWord = word.toUpperCase()
const isAlreadyHighlighted = highlightedWords.includes(currentWord)
if ((currentWord.includes(wordFromSearch.toUpperCase()) && this.state.currentSearch.length > 0) || isAlreadyHighlighted) {
let v = (
<Text key={index} style={{ color: '#2eb872' }}>
{word}{' '}
</Text>
)
if (!isAlreadyHighlighted) {
highlightedWords.push(currentWord)
}
suggestion[index] = v
} else {
let v = <Text key={index}>{word} </Text>
suggestion[index] = v
}
})
)
}
return (
<TouchableOpacity
style={Style.suggestionView}
onPress={() => {
this.props.UserStore.addRecentSearch(item)
this.props.NavigationStore.navigate({ routeName: 'SearchResult', params: { search: item } })
this.autoCompleteTimeout = setTimeout(() => {
if (mounted) this.setState({ showAutoComplete: false })
}, 400)
}}
>
<Icon name='search' size={20} style={{}} />
<Text style={{ marginLeft: 20, textAlign: 'left', color: '#9B999A' }}>{suggestion}</Text>
</TouchableOpacity>
)
}
getSuggestions = async currentSearch => {
try {
const response = await Api.serachOutoCompleate(currentSearch)
let searchAutoComplete = response.suggestions.products.map(product => product.product_title)
response.suggestions.categories.forEach(categories => searchAutoComplete.push(categories))
response.suggestions.warehouses.forEach(warehouse => searchAutoComplete.push(warehouse.warehouse_name))
response.suggestions.upcs.forEach(upcs => searchAutoComplete.push(upcs.product_title))
response.suggestions.tags.forEach(tags => searchAutoComplete.push(tags.product_title))
this.storedResults[currentSearch] = searchAutoComplete
if (mounted && currentSearch && searchAutoComplete) this.setState({ currentSearch: currentSearch, searchAutoComplete: searchAutoComplete })
else this.setState({ currentSearch: currentSearch })
} catch (error) {
console.log(error)
}
}
_onSearchChange = _.debounce(currentSearch => {
if (currentSearch === '') {
this.setState({ filter: [], currentSearch })
} else {
if (this.storedResults[currentSearch]) {
this.setState({ currentSearch })
let searchAutoComplete = this.storedResults[currentSearch]
if (mounted && currentSearch && searchAutoComplete) this.setState({ searchAutoComplete })
} else {
this.getSuggestions(currentSearch)
}
}
}, 250)
render() {
I18n.locale = this.props.UserStore.user.lang
const recent = this.props.UserStore.RecentSearches
return (
<View style={Style.container}>
<View style={Style.search_container}>
<TextInput
style={Style.search_input}
underlineColorAndroid='transparent'
placeholder={I18n.t('search_products')}
returnKeyType='search'
autoCorrect={false}
onChangeText={this._onSearchChange}
onFocus={() => this.setState({ showAutoComplete: true })}
onSubmitEditing={event => {
if (event.nativeEvent.text.length) this.props.UserStore.addRecentSearch(event.nativeEvent.text)
this.props.NavigationStore.navigate({ routeName: 'SearchResult', params: { search: event.nativeEvent.text } })
}}
onKeyPress={() => this.suggestionTimeout && clearTimeout(this.suggestionTimeout)}
blurOnSubmit
/>
</View>
{this.state.currentSearch.length > 0 && this.state.showAutoComplete && this.state.searchAutoComplete.length > 0 ? (
<View style={{ paddingVertical: 10 }}>
<FlatList initialNumToRender={20} data={this.state.searchAutoComplete} keyExtractor={(item, index) => item.toString()} renderItem={this._renderSuggestion} keyboardShouldPersistTaps='always' />
</View>
) : (
<View>
{recent.length > 0 ? (
<View>
<View style={Style.whiteBorder} />
<View style={Style.searchHistory}>
<Text style={Style.searchHistory_header}>{I18n.t('recent_searches')}</Text>
{recent.map((title, index) => (
<Button
key={index}
style={Style.recentSearch}
onPress={() => {
this.props.UserStore.addRecentSearch(title)
this.props.NavigationStore.navigate({ routeName: 'SearchResult', params: { search: title } })
}}
>
<Icon name='schedule' style={Style.recentSearchIcon} />
<Text style={Style.recentSearchText}>{title}</Text>
</Button>
))}
</View>
</View>
) : null}
</View>
)}
</View>
)
}
}
export default SearchProducts
I expect the output will show fade in animation in the flatlist
and i dont know how to implement it
This can be accomplished by using the Animated api.
First import Animated from react-native, then add fade: new Animated.Value(0) to your state inside the constructor.
Now change the View that is surrounding the FlatList from this
<View style={{ paddingVertical: 10 }}>
to this
<Animated.View style={{ paddingVertical: 10, opacity: this.state.fade }}>
At last add this block to start the animation when the list mounts:
componentDidMount() {
Animated.timing(this.state.fade, {
duration: 500,
toValue: 1,
useNativeDrivers: true
}).start();
}
If you want to animate the FlatList in every time the user makes a search, you would have to move this block to another part of your program logic and remember to set the fade value back to 0 before the animation.
I am using FlatList to write an infinite scroll, but it keeps sending request to my server forever. please see the code blow. I don't find any article clarify when the next page will load, what exactly does the onEndReached will be triggered.
import React, { Component } from 'react';
import { View, Text, FlatList, StyleSheet, ActivityIndicator, AsyncStorage } from 'react-native';
import { connect } from 'react-redux';
import { loadOrders } from '../redux/modules/Order';
import OrderListItem from './OrderListItem';
import { forOwn, isEmpty, reduce } from 'lodash';
class OrderList extends Component {
constructor(props) {
super(props);
this.state = {
page: 1,
error: null,
};
}
componentDidMount() {
this.loadOrders();
}
loadOrders = () => {
const { page } = this.state;
AsyncStorage.getItem("userToken")
.then((value) => {
return `Bearer ${value}`;
})
.then((userToken) => {
return this.props.loadOrders(page, { Authorization: userToken });
})
.then((response) => {
this.setState({
error: response.error || null,
});
})
.catch(error => {
this.setState({ error});
})
;
}
handleLoadMore = () => {
this.loadOrders();
};
onPressItem = (id: string) => {
};
keyExtractor = (item, index) => `order-item-${item.id}`;
renderItem = ({item}) => (
<OrderListItem
order={item}
onPressItem={this.onPressItem}
/>
);
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
/>
);
};
renderFooter = () => {
if (!this.props.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
const { orders} = this.props;
if (orders.length> 0) {
return (
<View containerStyle={styles.container} >
<FlatList
data={orders}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
ListFooterComponent={this.renderFooter}
ItemSeparatorComponent={this.renderSeparator}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.5}
/>
</View>
);
}
return <View>
<Text>empty</Text>
</View>
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
borderTopWidth: 0,
borderBottomWidth: 0
},
item: {
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#ccc'
}
});
const mapStateToProps = state => {
let order = state.get('order').toJS();
return {
orders: isEmpty(order.entities) ? [] : reduce(order.entities, (result, value) => {
result.push({ key: value.id, ...value });
return result;
}, []),
loading: order.loading
};
};
const mapDispatchToProps = {
loadOrders
};
export default connect(mapStateToProps, mapDispatchToProps)(OrderList);
the if part is false , but the onEndReached methods is still called, I must be insane.
the
Change this
onEndReachedThreshold={0.5}
to this:
onEndReachedThreshold={0}
Right now you're calling the end reached when you're halfway through. You can also try adding this to the FlatList:
legacyImplementation = {true}
If this still won't work I would recommend doing the 'pull' onRefresh. A nice example for you: https://www.youtube.com/watch?v=pHLFJs7jlI4
i met the problem too, in my case:
renderFooter somethings render null(height: 0) when loaded, but render ActivityIndicator when loading, and ActivityIndicator has its heigth bigger than 0(null's height)
when heigth change from 0 to ActivityIndicator's height, it will call onEndReached again
and you say the if part is false, i think its because it's not really false。
when code really run in FlatList, the if part is true, so it call onEndReached, and then the _scrollMetrics.contentLength or this._sentEndForContentLength has changed for some reason before your console in chrome. which makes the if part return false
above is all my thought for now, and i am still debugging for this problem, hope this answer will help you all
I have a component that allow user to press on "Add" button to dynamically add View component. I have managed to do the adding but how i can improve the code to remove dynamically added view component?
import React, { Component } from 'react';
import {
View,
Text, Platform,
StyleSheet,
TouchableOpacity,
Animated,
ScrollView,
Image,
Button
} from 'react-native';
export default class App extends Component<{}>
{
constructor()
{
super();
this.state = { valueArray: [], disabled: false }
this.index = 0;
this.animatedValue = new Animated.Value(0);
}
addMore = () =>
{
this.animatedValue.setValue(0);
let newlyAddedValue = { index: this.index }
this.setState({ disabled: true, valueArray: [ ...this.state.valueArray, newlyAddedValue ] }, () =>
{
Animated.timing(
this.animatedValue,
{
toValue: 1,
duration: 500,
useNativeDriver: true
}
).start(() =>
{
this.index = this.index + 1;
this.setState({ disabled: false });
});
});
}
render()
{
const animationValue = this.animatedValue.interpolate(
{
inputRange: [ 0, 1 ],
outputRange: [ -59, 0 ]
});
let newArray = this.state.valueArray.map(( item, key ) =>
{
if(( key ) == this.index)
{
return(
<Animated.View key = { key } style = {[ styles.viewHolder, { opacity: this.animatedValue, transform: [{ translateY: animationValue }] }]}>
<Text style = {styles.text}>Row { item.index }</Text>
<Button style={styles.button}>
<Text style = {styles.text}>Remove</Text>
</Button>
</Animated.View>
);
}
else
{
return(
<View key = { key } style = { styles.viewHolder }>
<Text style = { styles.text }>Row { item.index }</Text>
<Button style={styles.button}>
<Text style = {styles.text}>Remove</Text>
</Button>
</View>
);
}
});
return(
<View style = { styles.container }>
<ScrollView contentContainerStyle = { this.scrollViewStyle }>
<View style = {{ flex: 1, padding: 4 }}>
{
newArray
}
</View>
</ScrollView>
<TouchableOpacity activeOpacity = { 0.8 } style = { styles.btn } disabled = { this.state.disabled } onPress = { this.addMore }>
<Image source = { require('./assets/add.png') } style = { styles.btnImage }/>
</TouchableOpacity>
</View>
);
}
}
You just need to remove it from the this.state.valueArray. If you know which index you want to remove you can do it as;
removeItem = (index) => {
this.setState(prevState => ({
valueArray: prevState.valueArray.splice(index, 1),
}));
}
Here, you are basically taking an index, create a new array from the previous one without said index and update the state.