react-native flatlist not rerendering - react-native

Funny one this.
My flatlist rerenders on iOS but not android. That is - the flatlist renders and changes when I click on an item as I would expect from my code.
Flatlist code:
<FlatList
data={this.state.languageDataSource}
extraData={this.state.refresh}
renderItem={({ item, index }) =>
<View>
{(Platform.OS === 'android') ?
<TouchableNativeFeedback onPress={() => this._onPress(item)} background={TouchableNativeFeedback.SelectableBackground()} >
<View>
<MenuItem
item={item}
english={this.state.english}
navigation={this.props.navigation}
generateTestHook={this.props.generateTestHook.bind(this)}
/>
</View>
</TouchableNativeFeedback>
:
<TouchableHighlight
onPress={() => this._onPress(item)}
underlayColor='gray'>
<MenuItem
item={item}
english={this.state.english}
navigation={this.props.navigation}
generateTestHook={this.props.generateTestHook.bind(this)}
/>
</TouchableHighlight>
}
</View>
}
keyExtractor={(item, index) => index.toString()}
/>
When we click we get
_onGPSPress = (_click) => {
this.state.gpsDataSource.forEach(element => {
if (element.code == _click.code) { element.selected = true } else { element.selected = false; this.setState({
refresh: !this.state.refresh
}) }
});
this.setState({
refresh: !this.state.refresh
})
//Todo: appropriate server calls
}
and it seems on logging that setstate does not work. Any ideas?
UPDATE:
This is to do with the way I am setting the state for the datasource which is:
gpsDataSource: [
{ selected: false, title: I18n.t('Settings_precise_11f271'), code: 'precise' },
{ selected: true, title: I18n.t('Settings_approximate_b23bbf'), code: 'approximate' },
{ selected: false, title: I18n.t('Settings_off_810357'), code: 'off' },
]
So how can I set the individual "selected" bools?

If you feel there is an issue with setState then please try below code:
this.setState(prevState => ({
refresh: !prevState.refresh
}));

Related

How can I render only first object from array? React Native Flatlist

How can I render only first object from Array?? I know that I need to put [0] but I don't know where exactly? I tried item[0] and item.answers[0]. Is there any other way of doing it? I don't know what else to write so system allow me to post my question. lol
const DATA = [
{
id: "2222",
question:
"In class, how often do you purposely encourage others around you?",
answers: [
{
answerText:
"Bully: Use physical aggression to get my own way. Pick on others a lot",
}
],
},
{
id: "3333",
question:
"In class, how often do you purposely encourage others around you?",
answers: [
{
answerText:
"Manipulating: Try to get others to do what you want by telling",
},
],
},
];
const Item = ({ item, onPress, style }) => {
return item.answers
.map((x) => x.answerText)
.map((value, i) => (
<TouchableOpacity
onPress={onPress}
style={[styles.item, style]}
keyExtractor={i}
>
<Text style={styles.title}>{value}</Text>
</TouchableOpacity>
));
};
const renderItem = ({ item }) => {
// const backgroundColor = item.id === selectedId ? "#6e3b6e" : "#f9c2ff";
return (
<Item
item={item}
onPress={() => {
console.log(item);
}}
style={{ backgroundColor: Colors.primary }}
/>
);
};
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
</SafeAreaView>
);
};
Render item function provide also index argument, you may check, if index!=0 than return null
This might help
const Item = ({ item, onPress, style }) => {
return <TouchableOpacity
onPress={onPress}
style={[styles.item, style]}
>
<Text style={styles.title}>{item.answers[0].answerText}</Text>
</TouchableOpacity>
};

How to get the index value of FlatList in the header using React-native

I need to get the index value of the FlatList used in header of a React native component.
Below is the code I used.I need the parameter rowItem in onPressAction function which is returned as undefined
constructor(props) {
super(props);
this.state = {
selectedItem: '',
headerData: [
{ image: require('common/assets/images/headers/homeicon.png') },
{ image: require('common/assets/images/headers/CustomerDetails2.png') }
]
}
}
componentDidMount() {
// Event Listener for orientation changes
Dimensions.addEventListener('change', () => {
this.setState({
orientation: isPortrait() ? 'portrait' : 'landscape'
});
});
this.props.navigation.setParams({
headerData: this.state.headerData,
selectedItem: this.state.selectedItem,
onPressAction: (item) => this.onPressAction()
});
}
onPressAction = (rowItem) => {
alert(rowItem);
}
static navigationOptions = ({ navigation }) => {
return {
headerTransparent: true,
headerLeft: (
<View style={{ flex: 1, flexDirection: 'row' }} >
<FlatList
horizontal={true}
extraData={navigation.getParam('selectedItem')}
data={navigation.getParam('headerData')}
renderItem={({ item, index }) => (
<TouchableOpacity onPress={navigation.getParam('onPressAction')} >
<Image style={styles.centerIcon} resizeMode='contain' source={item.image}></Image>
</TouchableOpacity>
)}
keyExtractor={(item, index) => index.toString()}
/>
</View >
),
headerTintColor: 'transparent',
};
};
Could anyone please help me
Insert ID into headerData and use it as index value.
headerData: [
{ id: 0 , image: require('common/assets/images/headers/homeicon.png') },
{ id: 1 , image: require('common/assets/images/headers/CustomerDetails2.png') }
]
...
}
renderItem={({ item, index }) => (
<TouchableOpacity onPress={() => alert(navigation.getParam('onPressAction'))} >
<Image style={styles.centerIcon} resizeMode='contain' source={item.image}></Image>
</TouchableOpacity>
)}

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?

ScrollToIndex in React-Native

In my application, I want to use scrollToIndex() which is a method of the FlatList component.
render() {
let { posts, featuredWorkouts, mainFeedIsLoading } = this.props;
let mainFeed = <SwiperContainer featuredWorkouts={featuredWorkouts} />;
let mainFeedData = (
<View>
<View style={{ marginBottom: 5 }}>
<SwiperContainer featuredWorkouts={featuredWorkouts} />
</View>
<FlatList
data={posts}
scrollToIndex={5}
extraData={[posts, mainFeedIsLoading]}
renderItem={item => this.renderPost(item)}
keyExtractor={item => item.shareId}
/>
</View>
);
if (mainFeedIsLoading) {
mainFeedData = (
<View style={styles.screenLoader}>
<ScreenLoader color="lightblue" size="large" />
</View>
);
}
return (
<View>
<ScrollView>{mainFeedData}</ScrollView>
</View>
);
}
As an example, when the page loads, I want to go to the 10th index. How can I achieve this? I tried the scrollToIndex() and it did not work.
you have to add ref to your flat list.
ref={(ref) => { this.list = ref; }}
Also you have to add to scroll index to your componentdidmount.
componentDidMount() {
this.list.scrollToIndex({animated: true, index: tempIndex, viewOffset:0,viewPosition:0});
}
Also you can use:
initialScrollIndex
Well in my case I have to do this to make scrolltoindex work properly,
1.add reference to flatlist:
`
ref={ref => {
this.flatListRef = ref;
}}
`
2. Call scrollToIndex() with timeout:
`
setTimeout(() => {
this.flatListRef.scrollToIndex({
animated: true,
index: this.state.pressedIndex
});
}, 500);
`
3. then call function again inside flatlist
onScrollToIndexFailed={() =>
{
this.scrollToIndex();
}
}

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