How can I set data in a state in FlatList? - react-native

I want to save product ID (item.id) in a state like productId.
I need to product Id for add product to cart.
When I click on TouchableOpacity working fine but productId always is 4.
I have three Item. The id of last item is 4 and first item is 2.
When I click on TouchableOpacity of product 1, id is 4 but should be 2.
I see IDs are OK When I print IDs in listView.
<FlatList
data={this.state.dataSource}
renderItem={({item}) =>
<View>
<View>
<Text>{item.title} - {item.type}</Text>
<Text>{item.id}</Text>
<TouchableOpacity onPress={this.decrementCount,()=>this.setState({productId:item.id})}>
<AntDesign name="minus" size={15} style={{color:'#fff'}}/>
</TouchableOpacity>
<TouchableOpacity onPress={this.incrementCount} activeOpacity={0.5}>
<AntDesign name="plus" size={15} style={{color:'#fff'}}/>
</TouchableOpacity>
</View>
</View>
}
/>

I think this line is your issue:
<TouchableOpacity onPress={this.decrementCount,()=>this.setState({productId:item.id})}>
First things first, you should define your render function on your class, and you should define your onPress function separate from that. For example:
class MyClass extends React.Component {
handlePress = (item) => {
this.decrementCount()
this.setState({productId: item.id})
}
renderItem = ({item, index}) => {
return (...code from above, but using this.handlePress rather than your onPress function as currently defined)
}
render() {
return (
<FlatList
data={this.state.dataSource}
renderItem={this.renderItem}
/>
)
}
}
I think the real reason that its not working for you right now is that you're trying to call this.setState as a callback in your onPress function. It would probably work as is if you changed your current onPress from:
onPress={this.decrementCount,()=>this.setState({productId:item.id})}
to
onPress={() => this.setState({productId:item.id}, this.decrementCount)}
as I'm fairly sure you can have a function called as a callback from this.setState.
Hope this helps!

Related

Is there a way to get the Title of the selected item from another component in React Native

I have two different components "HomePage" & "ListItemCA"
In HomePage, I have a FlatList and a modal popup
<FlatList
data={ listData}
keyExtractor={list => list.Title}
renderItem={({ item }) => <ListItemCA data={item} onLongPress={openModal} />}
/>
and each list item is called from another component ListItemCA
function ListItemCA({data, onLongPress}) {
return (
<TouchableOpacity onLongPress={onLongPress} >
<View style={styles.container}>
<Text style={styles.title}>{data.Title}</Text>
<View style={styles.metaContainer}>
<Text style={styles.meta}>{( data.totalMonths != null ? data.totalMonths : '0' )} Months</Text>
<Text style={styles.meta}>{( data.members != null ? data.members.length : '0' )} Members</Text>
</View>
</View>
</TouchableOpacity>
);
}
What I want to acheive?
I want to get the selected list item title on my HomePage component. When a user longpress on a list item that title should be displayed on a modal popup. How do I pass the selected list item title to the HomePage component using longpress?
If your goal is to display data from the long pressed item in the modal, you could add the data as a parameter of your openModal function:
function openModal(data) {
// your function
return (
<Text>{data.Title}</Text>
)
}
Then, in your FlatList, modify the props of ListItemCA to call openModal for the selected item:
renderItem={({ item }) => <ListItemCA data={item} onLongPress={openModal(item)} />}
If you also want to save the data from the long pressed item in your HomePage component for other uses, you could save it in the state. In your HomePage component:
import React, { useState } from 'react'
function HomePage() {
const [itemData, setItemData] = useState()
// your code
}
Then, in your flatlist:
<FlatList
data={listData}
keyExtractor={list => list.Title}
renderItem={({ item }) =>
<ListItemCA
data={item}
onLongPress={ () => {
setItemData(item)
openModal(item)
}}
/>
}
/>
You can achieve this by passing(return) parameter from your component like this -
function ListItemCA({data, onLongPress}) {
return (
<TouchableOpacity onLongPress={() => {
onLongPress(data.Title);
//return data.Title when onLongPressed clicked
}}>
<View style={styles.container}>
...
</View>
</TouchableOpacity>
);
}
then get it in props -
<FlatList
data={listData}
keyExtractor={list => list.Title}
renderItem={({ item }) =>
<ListItemCA
data={item}
onLongPress={(title) => {//this **title** return from **onLongPress(data.Title)**
openModal();
setTitle(title);// or directly can pass that title in openModal func.
}}
/>
}
/>

How to prevent flatlist header or footer from re-render in reactnative

I put an input field in the footer of flatlist but when i try to type anything it dismiss the keyboard automatically because of the re-render of the flatlist footer..
I tried to nest the flatlist from Scrollview but this brings warning..
How can i stop the footer from being re-rendered? can i fix this without nest the flatlist from Scrollview?
<FlatList
ListHeaderComponent={() => (
<View style={styles.discountContainer}>
<Text style={[styles.buttonText, { letterSpacing: 3 }]}>
10% DISCOUNT ON 8 COURSES
</Text>
</View>
)}
numColumns={2}
data={data}
renderItem={({ item }) => (
<View>
<SingleProduct item={item} />
</View>
)}
ListFooterComponent={() => (
<View>
<View style={styles.couponContainer}>
<Input
placeholder='Coupon code'
placeholderTextColor='#0a5796'
color='#0a5796'
inputStyle={{
color: '#0a5796',
}}
inputContainerStyle={{
borderBottomWidth: 0,
height: 50,
}}
containerStyle={styles.couponInputContainer}
onChangeText={(value) =>
this.setState({ couponCode: value })
}
value={this.state.couponCode}
/>
{couponLoading ? (
<View style={styles.couponButton}>
<ActivityIndicator />
</View>
) : (
<TouchableOpacity
style={styles.couponButton}
onPress={() => this.codeCheck(couponCode, line_items)}
>
<Text style={styles.buttonText}>Apply Coupon</Text>
</TouchableOpacity>
)}
</View>
</View>
)}
/>
Arrow-Funktions are "always" executed and create a new Reference in Memory. This way they will always re-rendering if component will be executed.
For performance reasons you better define your function outside and call it like this:
function renderMyItem(){ ...bimbom... yous stuff goes here! }
function renderHeader(){ ...bimbom... yous stuff goes here! }
<Flatlist
renderItem={this.renderMyItem()}
ListHeaderComponent={this.renderHeader()}
...
/>
What happens here?
Both of your functions renderMyItem and renderHeader will be executed once if your component is loaded and will be saved in memory. So every time you call one of the functions, you call a reference to the place in memory where they are saved.
In the other case, Arrow-Functions ()=>{...} are executed in current context and generate a new reference in Memory, each time they called, because .. to say it clear: you define & call a function that way.
If you are using Functional Component then don't use Arrow function () => (...) for header and footer components of FlatList but instead only return your header and footer Components as shown in the sample below.
<FlatList
...
ListHeaderComponent={(<View><Text>Header</Text></View>)}
ListFooterComponent={(<View><Text>Footer<Text></View>)}
/>
I was going through the same problem and the accepted answer didn't worked for me. As here the problem occurs because we are updating the state whenever the text changes (as defined in onChangeText) which causes re-rendering. Thus i came up with another solution;
First i created another dict object newState which has nothing to do with state or props. So on changing newState dict, it will not cause re-rendering. Then;
newState = {}
<TextInput onChangeText={text => this.newState.value = text} />
Then i changed the state(which is necessary as per your problem and as per mine) on onEndEditing ;
<TextInput onChangeText={text => this.newState.value = text} onEndEditing={this.setSearch} />
Here is setSearch
setSearch= () => {
this.setState({couponCode: this.newState.value})
delete this.newState.value;
}
I am deleting the key after the state is set because it doesnot update correctly next time.

FlatList's renderItem doesn't recognise "this" keyword

So, I recently started making FlatList a recurring thing in the app I'm working on. I am right now working on a screen that gives a list of requests and is updated once one is accepted, which is done by pressing a button. There's a method called getNewRequests I am using to update the requests, but it can't seem to be called by the flatline, as it only returns the error TypeError: _this3 is undefined.
I really need that method to work, because I need to update the state of that screen, and trying to type the whole method there only returns the same error. In that context, this always returns undefined.
render(){
return(
<View style={GenericStyles.styles.genericContainer}>
<Text> REQUEST SCREEN </Text>
<FlatList
data={this.state.requestList}
renderItem={this.renderItem}
keyExtractor={item => item.id}
/>
<Button title="Voltar" color="cyan" onPress={() => this.props.navigation.goBack()}/>
</View>
);
}
renderItem({item}){
return(
<Card
containerStyle={{flex: 1, width: 200}}
title={item.Username}>
<Button color="blue" title="Accept" onPress={() => RequestService.allowRequest(item.id, (response) => {
let rsp = JSON.parse(response);
if(rsp.success){
this.getNewRequests();
}
})}/>
</Card>
);
}
You need to either bind the function in your constructor (or wherever you want) doing:
constructor(props){
super(props)
this.renderItem.bind(this)
}
or use arrow function:
renderItem = ({item}) => {
//your function
}
Doing this will give the function access to the this of the current component.

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

How to get the style name and state name of a clicked element in React Native?

In the following, first example how do I check if the clickable element TouchableWithoutFeedback has the style named EntryBlock1?
render() {
return (
<TouchableWithoutFeedback style={styles.EntryBlock1} onPress={() => this.CheckIfHasStyles()}>
<View style={styles.redundantWrapperNumber1}>
<Text style={styles.redundantWrapperNumber2}>Click To Test For Style</Text>
</View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback style={styles.EntryBlock2} onPress={() => this.CheckIfHasStyle()}>
<View style={styles.redundantWrapperNumber1}>
<Text style={styles.redundantWrapperNumber2}>Click To Test For Style</Text>
</View>
</TouchableWithoutFeedback>
); // end return
} // end render
In the 2nd example, how can I check if TouchableWithoutFeedback has the state name EntryBlock1 ?
constructor (props) {
super(props);
this.state = {
EntryBlock1: [styles.entryBlockButton, styles.entryBlockButtonMin],
EntryBlock2: [styles.entryBlockButton, styles.entryBlockButtonMax],
}
}
render() {
return (
<TouchableWithoutFeedback style={this.state.EntryBlock1} onPress={() => this.CheckIfHasStateName()}>
<View style={styles.redundantWrapperNumber1}>
<Text style={styles.redundantWrapperNumber2}>Click To Test For Style</Text>
</View>
</TouchableWithoutFeedback>
); // end return
What I've Tried
Different stuff along the lines of the following..
CheckIfHasStyles =()=> {
var object = this.state;
var stringifiedObject = JSON.stringify(object);
var slicedObject = stringifiedObject.slice(2, 13);
if (slicedObject === 'EntryBlock1') {
alert('success');
} else {
alert('failure');
}
}
.. which actually works for one very specific example, but not when I have multiple state names as this.state gets all of them in one object and not just the state of the element clicked.
Note: My question is solely about getting attributes and their values. It is to help me develop a style of toggling styles on elements but I am only looking for attribute stuff in these answers as I would like to use attributes, if possible, for more than just toggling in the future.
You can pass it as parameter to your function directly like this:
render() {
return (
<TouchableWithoutFeedback style={styles.EntryBlock1} onPress={() => this.CheckIfHasStyles('EntryBlock1')}>
<View style={styles.redundantWrapperNumber1}>
<Text style={styles.redundantWrapperNumber2}>Click To Test For Style</Text>
</View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback style={styles.EntryBlock2} onPress={() => this.CheckIfHasStyle('EntryBlock2')}>
<View style={styles.redundantWrapperNumber1}>
<Text style={styles.redundantWrapperNumber2}>Click To Test For Style</Text>
</View>
</TouchableWithoutFeedback>
);
}
and check if you are receiving any parameter in your function and do what you want
based on If-else condition