Expo : Select Multiple item of flatlist ( change style) - react-native

I'm trying to add a badge on a View ,by a TouchableOpacity on press.
The trouble is the animation work appears only on one element at a time and not on two or more simultaneously
Here the code :
Flatlist :
<FlatList
data={this.state.ReturnedArray}
width='100%'
ItemSeparatorComponent={this.FlatListItemSeparator}
keyExtractor={(item, index) => 'key' + index}
renderItem={this.renderizza}
extraData={this.state.activeItem}
/>
Render funtion :
<View style={style.containerFlat}>
<View style={style.containerFlat1}>
<Text style={style.txt}>{item.name} {item.cognome}</Text>
</View>
<TouchableOpacity style={style.containerFlat2} onPress={() => this.badge(item.expoToken,index)}>
{(this.state.activeItem === index) ? <Animated.View style={[style.animatedView, { opacity: this.state.fadeValue }]}><Text>ADDED</Text></Animated.View> : null}
<Text style={style.AvatarTxt} >{acronym}</Text>
</TouchableOpacity>
</View>
Badge function add Token into an array and lunch animation :
badge(chiavi, index) {
if ((this.state.SelectedUser).includes(chiavi)) {
this.Nonanimate(index)
this.state.SelectedUser.splice(this.state.SelectedUser.indexOf(chiavi), 1)
} else {
this.state.SelectedUser.push(chiavi)
this.animate(index)
}
}
and the animation / NotAnimation :
animate = (index) => {
this.setState({
activeItem: index,
});
Animated.timing(this.state.fadeValue, {
toValue: 1,
duration: 0
}).start()
};
Nonanimate = index => {
this.setState({
activeItem: index,
});
Animated.timing(this.state.fadeValue, {
toValue: 0,
duration: 0
}).start()
};
i'm wasting days on this trouble. Suggest ?

Here the function to solve this :
badge = chiavi => {
//console.log(chiavi.item.expoToken)
if ((this.state.SelectedUser).includes(chiavi.item.expoToken)) {
// this.Nonanimate(index)
this.state.SelectedUser.splice(this.state.SelectedUser.indexOf(chiavi.item.expoToken), 1)
} else {
this.state.SelectedUser.push(chiavi.item.expoToken)
// this.animate(index)
}
chiavi.item.isSelect = !chiavi.item.isSelect;
chiavi.item.selectedClass = chiavi.item.isSelect ?
style.selected : style.containerFlat1;
const index = this.state.ReturnedArray.findIndex(
item => chiavi.item.user_id === item.user_id
);
this.state.ReturnedArray[index] = chiavi.item;
this.setState({
ReturnedArray: this.state.ReturnedArray,
});
and Flatlist :
<FlatList
data={this.state.ReturnedArray}
width='100%'
ItemSeparatorComponent={this.FlatListItemSeparator}
keyExtractor={item => item.user_id.toString()}
renderItem={item => this.renderizza(item)}
extraData={this.state}
/>
Source : Link here

Related

Animate Single item on flatlist

How i can animate a single item from flatlist ?
I tryed adding a value to isolate the selected record but didnt' work,maybe i wrote something wrong. Atm the animation is on ALL records
Flatlist:
<View style={style.container}>
<FlatList
data={this.state.ReturnedArray}
width='100%'
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={this.renderizza}>
Render Flalist :
renderizza = (item) => {
var str = JSON.parse(JSON.stringify(item.item.itemType)).name + ' ' + JSON.parse(JSON.stringify(item.item.itemType)).cognome
let acronym = str.split(/\s/).reduce((response, word) => response += word.slice(0, 1), '')
return (
<View style={style.containerFlat}>
<View style={style.containerFlat1}>
<Text style={style.txt}>{JSON.parse(JSON.stringify(item.item.itemType)).name} {JSON.parse(JSON.stringify(item.item.itemType)).cognome}</Text>
</View>
<TouchableOpacity style={style.containerFlat2} onPress={() => this.badge(JSON.parse(JSON.stringify(item.item.itemType)).expoToken)}>
<Animated.View style={[style.animatedView, { opacity: this.state.fadeValue}]}><Text>ADDED</Text></Animated.View>
<Text style={style.AvatarTxt} >{acronym}</Text>
</TouchableOpacity>
</View>
)
}
And here the animation :
badge(chiavi) {
console.log(chiavi)
if ((this.state.SelectedUser).includes(chiavi)) {
this.state.SelectedUser.splice(this.state.SelectedUser.indexOf(chiavi), 1)
Animated.timing(this.state.fadeValue, {
toValue: 0,
duration: 0
}).start();
} else {
this.state.SelectedUser.push(chiavi)
Animated.timing(this.state.fadeValue, {
toValue: 1,
duration: 0
}).start()
}
}
Hot to fix ?
Thank you
One way is to maintain an active item in the state and use extraData prop for this variable in Flatlist.Then conditionally render between the Animated and normal Item.
animate = index => {
this.setState({
activeItem: index,
});
//Rest of animation code.
};
<TouchableOpacity onPress={e => this.animate(index)}>
{this.state.activeItem === index && (
<Animated.View
style={[
styles.button,
{ transform: [{ scale: this.animationMap }] },
]}
>
<Text>{item.title}</Text>
</Animated.View>
)
}
{this.state.activeItem !== index && (
<View style={styles.button}>
<Text>{item.title}</Text>
</View>
)
}
</TouchableOpacity>
Here is an sample Demo

react-native changes the properties of the elements in the array?

I have a FlatList and I want to implement a radio button.My idea is to change the selected property of an element in this.state.data to control it,but I am a newbie, I don't know how to change the property of an element in this.state.data.
Here is my code:
this.state = {
data: [
{
month:1,
price:18,
selected:true
},
{
month:3,
price:48,
selected:false
},
{
month:12,
price:128,
selected:false
},
],
};
<FlatList
data={this.state.data}
renderItem={({item, index, separators}) => (
<TouchableOpacity onPress={() => this.radio(index,item)}>
<View style={item.selected ? {borderWidth:3,borderColor:'#FFA371',borderRadius:15}:{}}>
<View style={styles.itemDefalut}>
<View style={{ flexDirection: "column", flex: 1 }}>
<Text>
Months
</Text>
<Text>{item.month} Months</Text>
</View>
<View>
<Text>${item.price}</Text>
</View>
</View>
</View>
</TouchableOpacity>
)}
/>
radio(index,item) {
for (var variable in this.state.data) {
variable.selected = false;
}
item.selected = true;
}
first pass only index from onpress
onPress={() => this.radio(index)
then in radio function do something like this
radio = index => {
let data = [ ...this.state.data ];
this.state.data.map((elem,key)=>{
if(elem.month==data[index].month){
data[key]={...data[key], selected: true};
}else{
data[key]={...data[key], selected: false};
}
})
this.setState({ data:data});
}
radio(item) {
let data = [...this.state.data];
let index = data.findIndex(el => el.month === item.month);
data[index] = {...data[index], selected: !item.selected};
this.setState({ data });
}
In TouchableOpacity on press it should be
<TouchableOpacity onPress = {this.radio.bind(this,item)}>

React-Native refresh Flatlist not working on Android

When a user presses a flatlist populated by gpsDataSource
this.state = {
gpsDataSource: [
{ selected: false, title: I18n.t('Settings_precise_11f271'), code: 'precise' },
{ selected: false, title: I18n.t('Settings_approximate_b23bbf'), code: 'approximate' },
{ selected: true, title: I18n.t('Settings_off_810357'), code: 'off' },
],
};
Populated through
<FlatList
data={this.state.gpsDataSource}
extraData={this.state.refresh}
renderItem={({ item, index }) =>
<View>
{(Platform.OS === 'android') ?
<TouchableNativeFeedback onPress={() => this._onGPSPress(item)} background={TouchableNativeFeedback.SelectableBackground()} >
<View>
<MenuItem
item={item}
navigation={this.props.navigation}
/>
</View>
</TouchableNativeFeedback>
:
<TouchableHighlight
onPress={() => this._onGPSPress(item)}
underlayColor='gray'>
<MenuItem
item={item}
onPress={() => this._onGPSPress(item)}
navigation={this.props.navigation}
/>
</TouchableHighlight>
}
</View>
}
keyExtractor={(item, index) => index.toString()}
/>
The user selects an item
_onGPSPress(_click) {
this.state.gpsDataSource.map(element => {
if (element.code == _click.code)
{console.log("match"); element.selected = true; return element }
else { console.log("no match");element.selected = false ; return element }
});
this.setState({
refresh: !this.state.refresh
})
//Todo: appropriate server calls
}
The flatlist refreshed on iOS but not Android (incidentally, Menuitem is identical for both platforms.
How can I get the flatlist (a selected blue point is in menu item) to refresh for Android?

React-Native scroll to index doesn't work

In my application, ScrollToIndex doesn't work as expected. Here is my code.
getItemLayout = (data, index) => (
{ length: 50, offset: 50 * index, index }
)
scrollToIndex = () => {
if(this.flatListRef != null){
setTimeout(() => this.flatListRef.scrollToIndex({animated: false, index: 5}), 200)
}
// this.flatListRef.scrollToIndex({animated: true, index: 5});
}
// render post
renderPost({ item, index }) {
return (
<Post
post={item}
likeIsLoading={
item.shareId === this.props.likeLoadingPostId &&
this.props.likeIsLoading
}
likeCount={item.likeCount > 0 ? item.likeCount : ""}
likeIcon={item.hasLiked === 1 ? LikeActive : LikeInactive}
name={item.hasLiked === 1 ? "like-active" : "like-inactive"}
onIconClick={payload => this.onIconClick(payload)}
/>
);
}
// render
render() {
let { posts, featuredWorkouts, mainFeedIsLoading } = this.props;
let mainFeedData = (
<View>
<View style={{ marginBottom: 5 }}>
<SwiperContainer featuredWorkouts={featuredWorkouts} />
</View>
<FlatList
ref={(ref) => { this.flatListRef = ref; }}
data={posts}
extraData={[posts, mainFeedIsLoading, featuredWorkoutsAreLoading]}
renderItem={(item, index) => this.renderPost(item, item.shareId)}
getItemLayout={this.getItemLayout}
// initialScrollIndex={4}
keyExtractor={item => item.shareId}
/>
</View>
);
if (mainFeedIsLoading) {
mainFeedData = (
<View style={styles.screenLoader}>
<ScreenLoader color="lightblue" size="large" />
</View>
);
}
return (
<View>
<Button
onPress={() => this.scrollToIndex()}
title="Tap to scrollToIndex"
color="darkblue"
/>
<ScrollView>{mainFeedData}</ScrollView>
</View>
);
}
I tried the suggestions which have been give on the internet, but none of them worked. What am I doing wrong here? How can I achieve what I want? As an example, I want to move to 5th index when the button is clicked.

Handle Multiselect in a GridView

I'm trying to handle the multi-select with react-native-super-grid , here is my code :
<GridView
itemDimension={80}
items={items}
style={styles.gridView}
renderItem={item => (
<View style={[styles.itemContainer , { backgroundColor:' transparent '}]}>
<TouchableHighlight style={styles.buttonStyle} onPress={() => this.pressEvent() }>
<Text> {item.image}</Text>
</TouchableHighlight>
<Text style={styles.buttonText}> {item.name}</Text>
</View>)}
/>
I tried using this function :
pressEvent(arr){
if(this.state.pressStatus == false){
this.setState({ pressStatus: true})
this.state.arr.push(arr)
this.setState({ color : 'white'})
} else {
this.setState({ pressStatus: false})
this.setState({ color: 'red'})
}
}
but it somehow doesn't work , can someone help me ?
Thank you .
This short example should give you an idea what are you doing wrong. The items itself are not aware of the state. So what I would do, I would create a separate child component for grid item and handle press state locally. Then handle parent, which is holding all the item trough callback about the pressed item.
class MyGridView extends Component {
render() {
return (
<GridView
itemDimension={80}
items={items}
style={styles.gridView}
renderItem={item => (
<GridItem
item={item}
onItemPress={selected => {
// set grid view callback
if (selected) {
//if true add to array
this.addToPressedArray(item);
} else {
//false remove from array
this.removeFromPressedArray(item);
}
}}
/>
)}
/>
);
}
// You don't change the state directly, you mutate it trough set state
addToPressedArray = item => this.setState(prevState => ({ arr: [...prevState.arr, item] }));
removeFromPressedArray = item => {
const arr = this.state.arr.remove(item);
this.setState({ arr });
};
}
And the GridItem
class GridItem extends Component {
// starting local state
state = {
pressStatus: false,
color: 'red'
};
// handle on item press
pressEvent = () => {
this.setState(prevState => ({
pressStatus: !prevState.pressStatus, //negate previous on state value
color: !prevState.pressStatus ? 'white' : 'red' //choose corect collor based on pressedStatus
}));
// call parent callback to notify grid view of item select/deselect
this.props.onItemPress(this.state.pressStatus);
};
render() {
return (
<View style={[styles.itemContainer, { backgroundColor: ' transparent ' }]}>
<TouchableHighlight style={styles.buttonStyle} onPress={() => this.pressEvent()}>
<Text> {item.image}</Text>
</TouchableHighlight>
<Text style={styles.buttonText}> {item.name}</Text>
</View>
);
}
}
I also recommend to read about React.Component lifecycle. Its a good reading and gives you a better understanding how to achieve updates.
Since GridView has been merged into FlatGrid. Therefore, I've implemented the multi-select option in a pretty easy way. First of all I applied TouchableOpacity on top of the view in the renderItems prop of FlatGrid like this.
<TouchableOpacity
onPress={() => this.selectedServices(item.name)}>
...props
</TouchableOpacity>
SelectedServices:
selectedServices = item => {
let services = this.state.selectedServices;
if (services.includes(item) == false) {
services.push(item);
this.setState({ selectedServices: services });
} else {
let itemIndex = services.indexOf(item);
services.splice(itemIndex, 1);
this.setState({ selectedServices: services });
}
};
Using splice, indexOf, and push, you can easily implement multi-selection.
To change the backgroundColor of the currently selected item, you can apply a check on the backgroundColor prop of the view.
renderItem={({ item, index }) => (
<TouchableOpacity
onPress={() => this.selectedServices(item.name)}
>
<View
style={[
styles.itemContainer,
{
backgroundColor: this.state.selectedServices.includes(
item.name
)
? '#0052cc'
: item.code
}
]}
>
<Text style={styles.itemName}>{item.name}</Text>
</View>
</TouchableOpacity>
)}