Change background of list item - reactjs

How can I change the background color of multiple list items when they are selected? I am using react-native-accordian and react-native-collapsible and using flat list within content.
_renderContent(section, i, isActive)
{
//console.log("MY DATA---",section.time_slots);
return (
<List
style={inStyles.body}
containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
data={section.time_slots}
renderItem={
({ item,index }) =>
(
<ListItem
onPress={() => this.selectSlot(item,section.date,index)}
style = {[inStyles.list , {marginLeft : 15}, {marginRight : 5},
{backgroundColor: (this.state.selectedItem[index]) ? 'green' : 'red'}]}
title={`${item}`}
containerStyle={{ borderBottomWidth: 0 }}
/>
)
}
keyExtractor={item => section.date+item}
ItemSeparatorComponent={this.renderSeparator}
ListFooterComponent={this.renderFooter}
/>
</List>
);
}
I just want to change the style of a list item dynamically using TouchableOpacity. But unable to do so.

You should change your code as below, your class would be like this:
contructor (props) {
super(props)
let selectedItemTemp = []
for(let i=0; i<section.time_slots.length; i++) { //section.time_slots is your FlatList data
selectedItemTemp.push(false)
}
this.state = {selectedItem: selectedItemTemp}
}
selectSlot = (item, section.date, index) => {
let {selectedItem} = this.state
selectedItem[index] = !selectedItem[index]
this.setState({selectedItem})
... // your other codes
}
render() {
return (
...
<FlatList
data={section.time_slots}
renderItem={
({ item, index }) => (
<TouchableOpacity
onPress={() => this.selectSlot(item,section.date, index)}>
<ListItem style = {[inStyles.list , {marginLeft : 15}, {marginRight : 5}, {backgroundColor: (this.state.selectedItem[index]) ? 'green' : 'white'}]}
title={`${item}`}
containerStyle={{ borderBottomWidth: 0 }}
/>
</TouchableOpacity>
)
} {item => section.date+item}
ItemSeparatorComponeultiple Selectiont={this.renderSeparator}
ListFooterComponent={this.renderFooter}
/>
...
);
}

Related

Mobx store do not update with observer

I have a simple react native app with two screens.
First screen is the list, where you see your selected groups, and you can remove them, by clicking on trash icon:
export const Settings: NavioScreen = observer(({ }) => {
...
return (
<View flex bg-bgColor padding-20>
<FlashList
contentInsetAdjustmentBehavior="always"
data={toJS(ui.savedGroups)}
renderItem={({item}) => <ListItem item={item} />}
estimatedItemSize={20}
/>
</View>
);
});
};
const ListItem = ({item}: any) => {
const { ui } = useStores();
return (
<View>
<Text textColor style={{ fontWeight: 'bold', fontSize: 15 }}>{item.name}</Text>
<TouchableOpacity onPress={() => ui.deleteGroup(item)}>
<Icon name={'trash'}/>
</TouchableOpacity>
</View>
);
};
The second screen is also the list, where you can add and remove the subjects from the list:
export const Playground: NavioScreen = observer(() => {
...
const groupsToShow =
ui.search && ui.search.length > 0
? ui.groups.filter((p) =>
p.name.toLowerCase().includes(ui.search.toLowerCase())
)
: ui.groups;
return (
<View >
<FlashList
data={toJS(groupsToShow)}
renderItem={({item}) => <ListItem item={item} />}
/>
</View>
);
});
const ListItem = ({item}: any) => {
const { ui } = useStores();
return (
<View>
<Text textColor style={{ fontWeight: 'bold', fontSize: 15 }}>{item.name}</Text>
<View>
<If
_={ui.isGroupSaved(item)}
_then={
<TouchableOpacity onPress={(e) => {ui.deleteGroup(item)}}>
<AntDesign name="heart" size={20} color={Colors.primary} />
</TouchableOpacity>
}
_else={
<TouchableOpacity onPress={(e) => {ui.addGroup(item)}}>
<AntDesign name="hearto" size={20} color={Colors.primary} />
</TouchableOpacity>
}
/>
</View>
</View>
);
};
And now when I remove the group from the first list, the heart icon do not update on the second list. But it should, because there is an if statement on second list, that checks if the group is saved. And if it is not, the heart should have the name="hearto"
I have tried to use the state instead mobx library but it does not also help.
Here is my store written with mobx:
export class UIStore implements IStore {
savedGroups = [];
constructor() {
makeAutoObservable(this);
makePersistable(this, {
name: UIStore.name,
properties: ['savedGroups'],
});
}
addGroup = (group: any) => {
if (true === this.isGroupSaved(group)) {
return;
}
this.savedGroups.push(group);
}
isGroupSaved = (group: any) => {
return -1 !== this.savedGroups.findIndex(g => g.id === group.id);
}
deleteGroup = (groupToDelete: any) => {
this.savedGroups = this.savedGroups.filter((group) => group.id !== groupToDelete.id);
}
}

How to get an event when the headers become stciky in SectionList?

I want to change the styles of the headers when they stick on the top of the SectionList. So I want some event that will let me know that the header are now sticking so as to change its style.
<SectionList
sections={this.state.list}
keyExtractor={(item, index) => item.id + index}
renderItem={({ item, index }) =>
<CustomItem
title={item.name}
description={item.appDescription}
>
</ExpandableItem>}
renderSectionHeader={({ section: { title, id } }) => (
this.state.list.length > 1 ? <View style={styles.sectionHeader} >
<Text style={styles.sectionHeaderTitle}>{title}</Text>
</View> : null
)}
ListHeaderComponent={() => {
return (<View style={{ paddingTop: 20 }} />);
}}
stickySectionHeadersEnabled={true}
removeClippedSubviews={true}
/>```
Use Animated Section list by
const AnimatedSectionList = Animated.createAnimatedComponent(SectionList);
then capture its offset like
const offset = new Animated.Value(0);
and use it in onScroll event.
Now on offset specific value change header styling using interpolate
I am adding a little example below using your code which changes height with scroll
<SectionList
sections={this.state.list}
keyExtractor={(item, index) => item.id + index}
onScroll={Animated.event(
[
{
nativeEvent: { contentOffset: { y: offset } },
},
],
{
useNativeDriver: true,
}
)}
renderItem={({ item, index }) =>
<CustomItem
title={item.name}
description={item.appDescription}
>
</ExpandableItem>}
renderSectionHeader={({ section: { title, id } }) => (
this.state.list.length > 1 ? <View style={styles.sectionHeader} >
<Text style={styles.sectionHeaderTitle}>{title}</Text>
</View> : null
)}
ListHeaderComponent={() => {
return (<View style={{ paddingTop: 20, height: offset.interpolate({
inputRange: [0, 30],
outputRange: [30, 0],
}) }} />);
}}
stickySectionHeadersEnabled={true}
removeClippedSubviews={true}
/>

Check visibility of item at the screen

I have FlastList with data. I need to check if item from the Flastlist is seen. So if i do not see data at my screen i do not have to do anything, but if i see i have to console.log data info. And when i am scrolling i have to console.log data that is visibile. I am trying to use onViewableItemsChanged with viewabilityConfig, but when i console.log data, it returns all data from FlastList but not data that is seen. Help me please.
I will be very thankfull!
_onViewableItemsChanged = ({ viewableItems, changed }) => {
console.log("Visible items are", viewableItems.map(item => item.item.text));
};
_viewabilityConfig = {
viewAreaCoveragePercentThreshold: 100
};
//....
<FlatList
data={this.state.postData}
initialNumToRender={0}
ItemSeparatorComponent = {this.FlatListItemSeparator}
renderItem={({item}) => {
return (
<View style={{paddingTop: 15}}
ref={ (divElement) => { this.divElement = divElement } }
>
// data
)}
}
onViewableItemsChanged={this._onViewableItemsChanged}
viewabilityConfig={this._viewabilityConfig}
keyExtractor={item => item.id}
/>
In your comment you specified that the FlatList is inside a view and scrollview. I tried to reproduce it this way:
<View style={styles.container}>
<ScrollView style={styles.scrollView}>
<FlatList
data={DATA}
initialNumToRender={0}
renderItem={renderItem}
onViewableItemsChanged={onViewableItemsChanged}
viewabilityConfig={viewabilityConfig}
keyExtractor={item => item.id}
/>
</ScrollView>
</View>
And indeed, console.log shows all data in this case. When I removed ScrollView so that the FlatList is inside the View element (with flex:1) then it works correctly - console.log shows only visible elements. My code:
const DATA = [
{
id: '1',
title: 'First Item',
},
// ... more elements
];
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const renderItem = ({item}) => {
return (
<View style={{paddingTop: 15}}
ref={ (divElement) => { this.divElement = divElement } }
>
<Item title={item.title} />
</View>
)
}
const viewabilityConfig = {
viewAreaCoveragePercentThreshold: 100
};
const onViewableItemsChanged = ({ viewableItems, changed }) => {
console.log("Visible items are", viewableItems.map(item => item.item.title));
};
export default function App() {
return (
<View style={styles.container}>
<FlatList
data={DATA}
initialNumToRender={0}
renderItem={renderItem}
onViewableItemsChanged={onViewableItemsChanged}
viewabilityConfig={viewabilityConfig}
keyExtractor={item => item.id}
/>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 32,
},
});

TouchableOpacity onPress() function doesn't works

I tried to create a DraggableList with some checkboxes, the problem is my component doesn't trigger the "onPress" while the "onLongPress" method working fine...
I tried to remove the "onLongPress" method, but still the same.
Here is my whole component :
class Menus extends React.Component {
state = {
menusItems: global.conf.Menus.map((d, index) => ({
id: d.id,
key: `item-${d.id}`,
label: d.label,
path: d.path,
value: d.value,
backgroundColor: `rgb(${Math.floor(Math.random() * 255)}, ${index *
5}, ${132})`,
})),
};
renderItem = ({item, index, drag, isActive}) => {
return (
<TouchableOpacity
style={{
backgroundColor: isActive ? 'blue' : item.backgroundColor,
alignItems: 'center',
justifyContent: 'center',
}}
onLongPress={drag}
onPress={() => console.log('puff')}>
<Checkbox
status={item.value ? 'checked' : 'unchecked'}
key={item.id}
title={item.label}
/>
</TouchableOpacity>
);
};
return (
<View style={{flex: 1}}>
<DraggableFlatList
data={this.state.menusItems}
renderItem={this.renderItem}
keyExtractor={(item, index) => `draggable-item-${item.key}`}
onDragEnd={({data}) => this.setMenus(data)}
/>
</View>
);
}
}
export default Menus;
I don't know what is wrong ....

Flatlist's children saying "Function components cannot be given refs."

I'm getting an error "Warning: Function components cannot be given refs. Attempts to access this ref will fail" when I attempt to add refs to the children of a Flatlist. However, it only seems to happen when I add the refs to the UserinfoGridComponent and not the View component.
Basically, what I believe is happening, from a bunch of googling, is that the flatlist's children are considered functional components, just as the error says, and as such cant accept refs. (but I'm confused because the child has setState...)
What I really need is just a ref inside the child (UserinfoGridComponent) so that I can scroll the flatlist as needed, from within the UserinfoGridComponent component. Is there a way to get this to work?
Parent:
class HomeScreen extends React.Component {
displayType = ({ item, index }) => {
const isBookmarked = (this.state.bookmarks.map(bookmark => bookmark.id).indexOf(item.id) !== -1)
return (
<View
ref={test => console.log("test ref 2", test)}
key={item.id}
style={[styles.shadowContainer, style={marginHorizontal: 15, marginBottom:25}]}>
<UserinfoGridComponent
ref={test => console.log("test ref 1", test)}
checked={ isBookmarked }
images={item.promoImages}
firstImg={item.promoImages ? (item.promoImages[0] || null) : null}
secondImg={item.promoImages ? (item.promoImages[1] || null) : null}
avatar={item.avatar || ''}
name={item.name || ''}
mainSkill={User.extractSkill(item.skill)}
secondarySkill={( item.otherSkills && item.otherSkills.length > 0 ) ? User.extractSkill(item.otherSkills[0]) : ''}
city={item.location.locality}
state={item.location.region}
// key={index}
index={index}
user={item}
onUserInfoPress={() => {
this.props.navigation.navigate('Profile', {
user: item,
clearAllFilters: this.clearAllFilters });
}}
onBookmarkPress={this.onBookmarkPress}
/>
</View>
)
}
render() {
const { creatorSearch } = this.props.data
return (
<View style={{flex:1, backgroundColor: 'white'}}>
<Animated.View style={{ opacity: this.state.initialOpacityAnimation, flex: 1}}>
<FlatList
ref={(list) => { this.listRef = list }}
contentInset={{top: 0, left: 0, bottom: 150, right: 0}}
scrollIndicatorInsets={{top: 60, left: 0, bottom: 70, right: 0}}
data={creatorSearch && this.state.initialContentLoaded ? creatorSearch.items : []}
onEndReachedThreshold={0.3}
onEndReached={() => this.scrollEnd()}
ListHeaderComponent={this.listHeaderText}
ListEmptyComponent={this.emptyListView}
extraData={this.state.bookmarks.length}
renderItem={this.displayType}
scrollEventThrottle={1}
/>
</Animated.View>
</View>
)
}
}
Child:
class UserInfoGridComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
loadedUser: { media: { items: [] }},
promoImages: this.props.user.promoImages
}
}
render() {
const combinedImages = this.state.loadedUser.media.items.length > 0 ? this.state.loadedUser.media.items : this.state.promoImages
return (
<View>
<FlatList
ref={ref => console.log("THIS IS A TEST", test)}
horizontal={true}
data={combinedImages}
renderItem={this.imageDisplay}
style={{padding:imageMargin*2}}
showsHorizontalScrollIndicator={false}
/>
</View>
}
reference should be assigned as ref={(ref) => this._flatlist = ref}
and you can access flatlist by using this._flatlist

Resources