ReactNative : Function called twice automatically inside FlatList - javascript

I have been in trouble seeking a solution to the silly problem. There is something missing from my code which I am unable to understand for now. Looking forward to your answers and information regarding the below code:
Constructor:
constructor(props) {
super(props)
this.TotalQuery = this.TotalQuery.bind(this);
this.state = {
isLoading: true,
Query: [],
}
this.UserID();
}
Function()
TotalQuery(product_id){
fetch(`http://:3000/api/v1/users/queries/${product_id}`, {
method: 'GET',
}).then((response) => response.json()).then((resp => {
this.setState({
Query: resp
})
})) .catch((error)=>{
console.log("Api call error1");
})
}
Calling this inside the Flatlist like below:
<FlatList
data={this.state.UserProducts}
keyExtractor={(item, index) => index.toString()}
renderItem= { ({item}) => (
<View style={styles.order}>
<View style={styles.orderHeader}>
<View style={styles.ohInfo}>
<Text style={styles.ohLabel}>Ref#</Text>
<Text style={styles.ohValue}>#2019-{item.product_id}</Text>
</View>
<View style={[styles.ohInfo, { backgroundColor: '#E7E7E7' }]}>
<Text style={styles.ohLabel}>Amount</Text>
<Text style={styles.ohValue}>€{item.price}</Text>
</View>
<View style={styles.ohInfo}>
<Text style={styles.ohLabel}>Messages</Text>
{this.TotalQuery(item.product_id)}
{this.state.Query.map((i, index) => (
<Text style={styles.ohValue}>{i.total}</Text>))}
</View>
</View>
<View style={styles.profileImgContainer}>
<View>
<ImageBackground style={styles.profileImgContainer}>
<Image source={{ uri: item.url }} style={[styles.profileImg]} />
</ImageBackground>
</View>
</View>
<View style={styles.orderBottom}>
<View style={styles.orderBottomLf}>
<Image resizeMode={'contain'} style={{ width: 14, height: 14 }}
source={require('../../assets/images/icon-pending-black.png')} />
<Text
style={[styles.orderStatusText]}>
{(item.status === 1) ? <Text style={styles.Approved}>Approved</Text> : null}
{(item.status === 2) ? <Text style={styles.Sold}>Sold</Text> : null}
{(item.status === 3) ? <Text style={styles.UnderReview}>Under Review</Text> : null}
{(item.status === 4) ? <Text style={styles.Inactive}>Inactive</Text> : null}
{(item.status === 5) ? <Text style={styles.Deleted}>Deleted</Text> : null}
</Text>
</View>
<View style={styles.orderBottomRg}>
<TouchableOpacity style={styles.profileImgContainer1} onPress={() => this.Sold(item.product_id)}>
{(item.status === 1) ? <Image style={[styles.profileImg1]} source={require('../../assets/images/checked.png')}/> : null}
</TouchableOpacity>
</View>
<View style={styles.orderBottomRg}>
<TouchableOpacity style={styles.profileImgContainer2} onPress={() => {this.Delete(item.product_id)}}>
{(item.status === 1 || item.status === 3 || item.status === 4) ? <Image style={[styles.profileImg2]} source={require('../../assets/images/delete.png')}/> : null }
</TouchableOpacity>
</View>
</View>
</View>
)}
/>
Above is the flatlist rendering, everything is rendering from it only. Please check.

There are multiple problems with your code.
The problem is that you are calling a function within the Flatlist renderItem method.
The way Flatlist works is you give it a data set and then it will call renderItem for each entry in that data set.
And then, any time your component re renders or the child item re renders the Flatlist will do this again.
Plus, it looks like you want to call this.TotalQuery(item.product_id) for each item in your data set but you are saving the return value to a single state value, so each call with overwrite the previous.
I would recommend moving your renderItem code into its own Component, and then each Component instance can have it's own state object where you can save the return value from your function call.

Related

React native : Flatlist inside scrollview

My goal is for this entire block to be scrollable.
I tried all kinds of ways to achieve the goal but without success.
I tried with ListHeaderComponent and moved the entire top view to it and it didn't work.
And I also tried <FlatList nestedScrollEnabled />
And it didn't work either.
What is the correct way to reach the scroll?
I come from here :
const renderAccordians = () => {
const items: JSX.Element[] = [];
areaData.forEach(item => {
items.push(<Accordian item={item} key={item.title} />);
});
return items;
};
To here :
return (
<View>
<View style={styles.row}>
<TouchableOpacity onPress={() => onClickFather()}>
<MaterialIcons size={24} name={data.checked ? 'check-box' : 'check-box-outline-blank'} color={'black'} />
</TouchableOpacity>
<Text style={[styles.title]}>{data.title}</Text>
<TouchableOpacity style={styles.row} onPress={() => toggleExpand()}>
<MaterialIcons name={expanded ? 'arrow-drop-up' : 'arrow-drop-down'} size={30} color={'black'} />
</TouchableOpacity>
</View>
<View style={styles.parentHr} />
{expanded && (
<FlatList
data={data.data}
numColumns={1}
scrollEnabled={false}
renderItem={({ item, index }) => (
<View>
<TouchableOpacity style={[styles.childRow, styles.button]} onPress={() => onClick(index)}>
<MaterialIcons
size={24}
name={item.checked ? 'check-box' : 'check-box-outline-blank'}
color={'black'}
/>
<Text style={[styles.itemInActive]}>{item.key}</Text>
</TouchableOpacity>
<View style={styles.childHr} />
</View>
)}
/>
)}
</View>
);
Since your FlatList will be part of an Accordion component, you "can't" embed the ExpandButton inside the Flatlist > ListHeaderComponent ... cause It'll simply hide the whole FlatList along with it's Header when you collapse your accorddion...
keyExtractor is also missing in your FlatList .. I added index as a key here which is not recommended BTW, you better use a unique field in your listItem like id...
return (
<View style={{ flex: 1}}> // <<--- Look here
<View style={styles.row}>
<TouchableOpacity onPress={() => onClickFather()}>
<MaterialIcons
size={24}
name={data.checked ? 'check-box' : 'check-box-outline-blank'}
color={'black'}
/>
</TouchableOpacity>
<Text style={[styles.title]}>{data.title}</Text>
<TouchableOpacity style={styles.row} onPress={() => toggleExpand()}>
<MaterialIcons
name={expanded ? 'arrow-drop-up' : 'arrow-drop-down'}
size={30}
color={'black'}
/>
</TouchableOpacity>
</View>
<View style={styles.parentHr} />
{expanded && (
<FlatList
data={data.data}
numColumns={1}
scrollEnabled={true} // <<--- Look here
keyExtractor={(_, index) => index.toString()} // <<=== Look here
contentContainerStyle={{flexGrow: 1}} // <<--- Look here
renderItem={({ item, index }) => (
<View>
<TouchableOpacity
style={[styles.childRow, styles.button]}
onPress={() => onClick(index)}
>
<MaterialIcons
size={24}
name={item.checked ? 'check-box' : 'check-box-outline-blank'}
color={'black'}
/>
<Text style={[styles.itemInActive]}>{item.key}</Text>
</TouchableOpacity>
<View style={styles.childHr} />
</View>
)}
/>
)}
</View>
);
If it does not work, I think you should create a component and use map datalist to render all the items and putting them into the ScrollView tag.
<ScrollView
style={styles.messageContain}
ref={ref => {
this.scrollView = ref;
}}
{data.data.map((item, index) => {
return <YourComponent key={index} data={item} />;
})}
</ScrollView>

Automatically scroll ScrollView in KeyboardAvoidingView when I add a new TextInput

I am working with a KeyboardAvoidingView and it's working perfectly fine except for one small issue. In the code below I have a TouchableOpacity that when clicked runs the function addName() which appends to an array and creates a new TextInput - basically when you click it, it adds a new TextInput to the ScrollView.
The KeyboardAvoidingView works perfectly fine except every time a new TextInput is added/rendered, I have to scroll down to see it. Do you know how I can make it automatically scroll to the bottom when a new TextInput is rendered?
Here is my code for the KeyboardAvoidingView:
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS == "ios" ? "padding" : "height"}
>
<HeaderComponent
name={this.props.route.params.bill.barName + " Tab"}
navigation={this.props.navigation}
goHome={true}
goHomePrompt={true}
/>
<View
style={{
marginTop: 30,
marginLeft: 10,
}}
>
<Text
style={{ color: "white", fontSize: 18 }}
allowFontScaling={false}
>
Add people to split with...
</Text>
</View>
<ScrollView keyboardShouldPersistTaps={"handled"}>
{this.state.nameInput.map((value, index) => (
<View style={styles.nameContainer} key={index}>
<View
style={{
width: "90%",
}}
>
<TextInput
style={styles.textInputContainer}
value={value}
onChange={this.handleText(index)}
placeholder={"Enter name..."}
placeholderTextColor={"#333333"}
maxLength={50}
/>
</View>
<View
style={{
width: "10%",
}}
>
<TouchableOpacity
onPress={() => this.handleDelete(index)}
>
<Icon name="cancel" type="material" color="red" />
</TouchableOpacity>
</View>
</View>
))}
<TouchableOpacity onPress={() => this.addName()}>
<Text style={styles.addPerson}>+ Add Person</Text>
</TouchableOpacity>
</ScrollView>
<View style={styles.bottomContainer}>
<TouchableOpacity
style={styles.continueButton}
onPress={() => {
// filters through array to make sure there are no empty strings
let nameInput = this.state.nameInput.filter(
(name) => name !== ""
);
if (
nameInput.length > 1 &&
new Set(nameInput).size === nameInput.length
) {
this.setState({ nameInput, loading: false });
} else {
alert(
"Please make sure there are at least two people and no duplicate names!"
);
}
}}
>
<Text style={styles.continueButtonText} allowFontScaling={false}>
Continue to Split Tab
</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
And here is my code for the addName() function:
addName = () => {
let nameInput = this.state.nameInput.concat("");
this.setState({
nameInput,
});
};
This page has the solution I was looking for: Is it possible to keep a ScrollView scrolled to the bottom?

Different content on React Native based on condition

on my react-native app I would like to have premium content for people who paid a subscription.
My issue is how I make the content to display as unavailable(if you are not premium ) and the same as the other content if you are premium. Basically I would like the premium content to be displayed with a "lock overlay" on it for non-premium users.
However, I do not know how I set this conditional. It is a matter of state? If yes where should be positioned this state considering that is unidirectional?
Just to be clear I will have premium and non premium content
class Browser extends Component {
scrollX = new Animated.Value(0);
renderRecommended = () => {
return (
<View style={[styles.flex, styles.column, styles.recommended]}>
<View style={[styles.row, styles.recommendedHeader]}>
<Text
style={{
fontSize: theme.sizes.font * 1.4,
alignSelf: 'center',
color: 'white',
fontFamily: 'Nunito-Bold',
}}>
Recommended
</Text>
</View>
<View style={[styles.column, styles.recommendedList]}>
<FlatList
horizontal
scrollEnabled
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
snapToAlignment="center"
style={[styles.shadow, {overflow: 'visible'}]}
data={this.props.destinations}
keyExtractor={(item, index) => `${item.id}`}
renderItem={({item, index}) =>
this.renderRecommendation(item, index)
}
/>
</View>
</View>
);
};
renderRecommendation = (item, index) => {
const {destinations} = this.props;
const isLastItem = index === destinations.length - 1;
const {navigation} = this.props;
return (
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate('PreScreen', {
item,
})
}>
<View
style={[
styles.flex,
styles.column,
styles.recommendation,
styles.shadow,
index === 0 ? {marginLeft: theme.sizes.margin} : null,
isLastItem ? {marginRight: theme.sizes.margin / 2} : null,
]}>
<ImageBackground
style={[styles.imageback]}
source={{uri: item.preview}}
/>
</View>
<View
style={[
index === 0 ? {marginLeft: theme.sizes.margin - 10} : null,
isLastItem ? {marginRight: theme.sizes.margin / 2} : null,
]}>
<Text
style={{
fontSize: theme.sizes.font * 1.25,
fontWeight: '200',
color: 'white',
marginLeft: 10,
//paddingBottom: 20,
fontFamily: 'Nunito-Bold',
}}>
{item.title}
</Text>
<Text
style={{
color: theme.colors.caption,
marginLeft: 10,
fontFamily: 'Nunito-SemiBold',
}}>
{item.location}
</Text>
</View>
</TouchableOpacity>
);
};
render() {
return (
<ScrollView style={styles.container}>
<BackgroundSvg style={styles.background} />
<ScrollView
style={styles.contentContainer}
showsVerticalScrollIndicator={false}
contentContainerStyle={{paddingBottom: theme.sizes.paddin}}>
{this.renderRecommended()}
{this.renderRecommended2()}
{/* <View style={styles.mainContainerView}>
<TouchableOpacity style={styles.singInButton} gradient>
<Text style={styles.logInText}>
Activate premium subscription
</Text>
</TouchableOpacity>
</View> */}
</ScrollView>
</ScrollView>
);
}
}
Browser.defaultProps = {
destinations: mocks,
reading: readingList,
};
export default Browser;
My code is the one on the top. Just to simplify I am accesing some elements from JSON and I am creating Flatlist based on this. What I want is is to give some of the JSON files a bolean with premium or not and in this way to make some elements available for user or not.
As part of your destinations array, for each object, you can specify a boolean field called isPremiumItem. Your users should have a boolean field like isPremiumUser to show the type of their subscription.
Then in renderRecommended method you can check the user subscription status (isPremiumUser), and also the specific item status (isPremiumItem). Then you can render accordingly.

How to navigate between different components based on conditions in react native

I am building a a mobile app in react native in which I have videos and audios coming in same data array. Now I am rendering them into a flatlist and audios and videos are coming together randomly. Now I want that If I click on the audio file it should navigate to Audios component and if I click on any video it should navigate to Videos Component. But I don't know how to filter and navigate to their respective components. Kindly help me. Thank you
My code
Main File: It is navigating to Audio component either I click on audio file or either on video file
<FlatList
horizontal
data={latestuploads}
keyExtractor={item => item.id}
renderItem={({item}) => {
return (
<ScrollView horizontal={true}>
<Card transparent style={{width: 170}}>
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate('Audio', {id: item.id})
}>
<CardItem>
<ImageBackground
source={{uri: item.image_url}}
style={styles.image}>
<Image
source={require('../assets/play-icon.png')}
style={styles.icon}
/>
</ImageBackground>
</CardItem>
</TouchableOpacity>
<CardItem cardBody>
<Text numberOfLines={1} style={styles.title}>
{item.title}
</Text>
</CardItem>
<CardItem cardBody>
<Text style={styles.speaker}> {item.speaker} </Text>
</CardItem>
</Card>
</ScrollView>
);
}}
/>
I suppose you're getting file extension example .mp4/.mp3 etc or Audio/Video flag from your data array.
Create a function that takes file info example:
navigateTo = (fileinfo) => {
// const filetype = check for file type, Audio/Video or file extension
if (filetype === 'Audio'){
this.props.navigation.navigate('Audio', {id: fileinfo.id})
} else {
this.props.navigation.navigate('Video', {id: fileinfo.id})
}
Pass this to your TouchableOpacity:
<TouchableOpacity
onPress={() => navigateTo(item)}>
// your code here
</TouchableOpacity>
constructor()
{
super(props)
this.state = {
ItemindexChecked: "",
}
this.Dataarrayholder = latestuploads;
}
............................your code ............
DataFilter(p_value)
{
const newData = this.Dataarrayholder.filter(function (item) {
const itemData = item.value ? item.value.toUpperCase() : ''.toUpperCase();
const textData = p_value.toUpperCase();
return itemData.indexOf(textData) > -1;
if (p_value != "")
{
if (newData == 0) {
return
}
else
{
this.props.navigation.navigate('Audio', { id: newData[0].id });
}
}
});
}
...........................................your code ..............
<FlatList
horizontal
data={latestuploads}
keyExtractor={item => item.id}
renderItem={({ item }) => {
return (
<ScrollView horizontal={true}>
<Card transparent style={{ width: 170 }}>
<TouchableOpacity
//onPress={() =>
// // this.props.navigation.navigate('Audio', { id: item.id })
// }
onPress={() => { this.DataFilter(item.value), this.setState({ ItemindexChecked: item.key }) }}
>
<CardItem>
<ImageBackground
source={{ uri: item.image_url }}
style={styles.image}>
<Image
source={require('../assets/play-icon.png')}
style={styles.icon}
/>
</ImageBackground>
</CardItem>
</TouchableOpacity>
<CardItem cardBody>
<Text numberOfLines={1} style={styles.title}>
{item.title}
</Text>
</CardItem>
<CardItem cardBody>
<Text style={styles.speaker}> {item.speaker} </Text>
</CardItem>
</Card>
</ScrollView>
);
}}
/>
maybe it helpful for you

React native specific style item in map

I have this code that generates buttons for size as come from API like (al, l, m, s ... ) and I need to put a specific style when user click in one circle, I have tried this code but it makes the same style for all circles and I need to change the style for one circle when clicked on it :
<View style={{ paddingTop: 10, width: "49%" }}>
<View style={styles.sizeView}>
<View style={styles.sizeView3}>
<Text style={styles.chartText}>{t("size")}</Text>
</View>
</View>
<View style={styles.sizeButtons}>
{this.state.productsList[0].sizes.map((item, index) => {
return (
<TouchableOpacity
key={index}
onPress={() => this.toggle1(item)}
style={
!this.state.pressStatus
? styles.sizes
: styles.sizesAlt
}
onHideUnderlay={this._onHideUnderlay.bind(this)}
onShowUnderlay={this._onShowUnderlay.bind(this)}
>
<Text
key={index}
style={
!this.state.toggle1 ? {} : { color: "#EC1C24" }
}
>
{item}
</Text>
</TouchableOpacity>
);
})}
</View>
</View>
I drag pic for the result
The issue is that you are using one variable to change the toggle status for all the buttons (this.state.toggle1), so if one is toggle they will all be toggled, as i don't think this is the intended behavior.
I suggest to create a pure component for the buttons so that each will have it's own state and handle the toggle independetly.
as for the style, they are 2 syntax working :
style={ !this.state.toggle ? {} : { color: "#EC1C24", backgroundColor: 'red' }}
or
style={[ this.state.toggle && {color: "#EC1C24", backgroundColor: 'red'} ]}
So first create a component for the circle button
export class CircleButton extends Component {
constructor(props) {
super(props);
this.state = {
toggled: false
};
}
render() {
return (
<TouchableOpacity
key={index}
onPress={() => this.toggle1(item)}
style={
!this.state.pressStatus
? styles.sizes
: styles.sizesAlt
}
onHideUnderlay={this.props.onHideUnderlay.bind(this)}
onShowUnderlay={this.props.onShowUnderlay.bind(this)}
>
<Text
key={index}
style={
!this.state.toggle ? {} : { color: "#EC1C24" }
}
>
{item}
</Text>
</TouchableOpacity>
);
}
}
And Change your View to something like this
<View style={{ paddingTop: 10, width: "49%" }}>
<View style={styles.sizeView}>
<View style={styles.sizeView3}>
<Text style={styles.chartText}>{t("size")}</Text>
</View>
</View>
<View style={styles.sizeButtons}>
{this.state.productsList[0].sizes.map((item, index) => {
return (
<CircleButton
onHideUnderlay={this._onHideUnderlay.bind(this)}
onShowUnderlay={this._onShowUnderlay.bind(this)}/>
);
})}
</View>
</View>

Categories

Resources