React native list map() method add custom element below selected Item - react-native

When Item with id=1 selected
then the element appear below
the selected Item. And when
unselected the element disappear.
This is a list with map() method.
The element should be inside the
SrollView like Item
But isn’t a new Item
I have this code that can create a new Item below the selected Item but I don't want to create a new Item but only appear a custom View(element) like above.
Expo Snack> https://snack.expo.dev/#stefanosalexandrou/honest-cashew

You could store the selected index in a state, which you are already doing. Then, use conditional rendering in order to render a custom component below the selected item. Furthermore, if a selected item is pressed again, set the state to undefined.
The handleOnPress function.
function handleOnPress(idx) {
setSelectedId(prev => prev === idx ? undefined : idx)
}
The updated render function.
<View style={styles.container}>
<ScrollView>
<View>
{data.map((person, index) => {
const backgroundColor = index === selectedId ? "#6e3b6e" : "#f9c2ff";
return (
<View>
<TouchableOpacity
onPress={() => handleOnPress(index)}
style={{
padding:20,
backgroundColor: backgroundColor,
marginBottom:20,
}}
>
<Text>{person.name}</Text>
</TouchableOpacity>
{
index === selectedId ? <View style={{backgroundColor: "red", height: 100, width: "100%"}}><Text>Custom Super Component Visible on press of above item </Text></View> : null
}
</View>
);
})}
</View>
</ScrollView>
</View>
I have added a dummy component if the index is selected.
However, you might want to select multiple items one after the other and deselect them individually while having the same effect. For doing so, we change the state to store an array of indices instead.
The updated state and handleOnPress function.
const [selectedIndices, setSelectedIds] = useState([]);
function handleOnPress(idx) {
if (selectedIndices.includes(idx)) {
setSelectedIds(prev => prev.filter(i => i !== idx))
} else {
setSelectedIds(prev => [...prev, idx])
}
}
The updated render function.
return (
<View style={styles.container}>
<ScrollView>
<View>
{data.map((person, index) => {
const backgroundColor = selectedIndices.includes(index) ? "#6e3b6e" : "#f9c2ff";
return (
<View>
<TouchableOpacity
onPress={() => handleOnPress(index)}
style={{
padding:20,
backgroundColor: backgroundColor,
marginBottom:20,
}}
>
<Text>{person.name}</Text>
</TouchableOpacity>
{
selectedIndices.includes(index) ? <View style={{backgroundColor: "red", height: 100, width: "100%"}}><Text>Custom Super Component Visible on press of above item </Text></View> : null
}
</View>
);
})}
</View>
</ScrollView>
</View>
);

Related

REACT NATIVE How can I highlight an item with a scrollview and increment/decrement the highlighted item?

I am building an 'Initiative Tracker' for home use. My goal is to have the first item at the top of the list highlighted by default and use a counter that keeps track of the turns to highlight the item to correspong with its turn.
Eg. When the '+' button is pressed, the next item should be highlighted and the previous item should return to normal.
I am currently using the map function to display an array. I feel like there should be a way to use the index and a style to achieve what I want, but I've had no luck so far.
Thanks in advance for any help or suggestions!
related code below:
let Encounter1Array = [
{ name: "goblin1", init: 5 },
{ name: "goblin2", init: 8 },
{ name: "goblin3", init: 15 },
{ name: "goblin4", init: 3 },
{ name: "goblin5", init: 9 },
];
function InitiativeTrackerScreen() {
const [encounter, setEncounter] = useState(Encounter1Array);
encounter.sort(function (x, y) {
return y.init - x.init;
});
return (
<KeyboardAvoidingView style={styles.wrapper}>
<ScrollView>
{encounter.map((item, index) => {
return (
<View key={index}>
<TouchableOpacity style={styles.ItemDisplayContainer}>
<View>
<View>
<Text style={{ fontStyle: "italic", fontSize: 16 }}>
{item.name}
</Text>
</View>
<View>
<Text style={{ fontSize: 12 }}>
Initiative: {item.init}
</Text>
</View>
</View>
</TouchableOpacity>
</View>
);
})}
</ScrollView>
</KeyboardAvoidingView>
);
}
You need extra state to keep track of the index of the element you want to highlight. Then you can use a conditional statement to match to right index and switch its style.
const P = ({ list }) => {
const [current, setCurrent] = useState(0);
return list.map((item, i) =>
<View style={i === current ? highlightStyle : style}>
<Button onPress={() => setCurrent(current + 1)}>
{"+"}
</Button>
{item}
</View>
);
};

Updating button state which is clicked, inside RenderItem of Flat-list, in React Native

I am new to React Native and trying to accomplish something like below, where simple list from server is rendering in a list with a button. Button, upon click, will be disabled and opacity will be changed.
I am able to create the UI but when I click any button that says Join, previous clicked button resets it state to original. In other words, only one button displays clicked state all the time.
so my code is something like
constructor(props){
super(props);
this.state = {selectedIndices: false, groupsData: ''};
}
Flatlist inside render method looks like
<FlatList style={styles.list}
data={this.state.groupsData}
keyExtractor={(groups) => {
return groups.groupId.toString();
}}
renderItem={(item, index) => {
return this.renderGroupItem(item);
}}/>
RenderGroupItem
renderGroupItem = ({item} )=>(
<GroupItem group = {item} style={{height: '10%'}} onPress = {() => this.onJoinButtonPress(item)}
index = {this.state.selectedIndices}/>
)
onJoinButtonPress
onJoinButtonPress = (item) =>{
this.setState({selectedIndices: true});
}
GroupItem
render(){
if(this.props.group.groupId === this.props.index){
return(
<View style = {[styles.container]}>
<Image source={{uri: 'some Image Url'}} style={styles.roundImage}/>
<Text style={styles.groupText}>{this.props.group.name}</Text>
<View >
<TouchableOpacity style = {[styles.button, {opacity: 0.4}]} activeOpacity = { .5 } onPress = {this.props.onPress}
disabled = {true}>
<Text style = {{color: 'white', fontSize: 12}}>Joined</Text>
</TouchableOpacity>
</View>
</View>
);
}else{
return(
<View style = {styles.container}>
<Image source={{uri: 'Some Image Url'}} style={styles.roundImage}/>
<Text style={styles.groupText}>{this.props.group.name}</Text>
<View >
<TouchableOpacity style = {styles.button} activeOpacity = { .5 } onPress = {this.props.onPress}>
<Text style = {{color: 'white', fontSize: 12}}>Join</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
Now I know that I need to pass an array or hasmap which contains mapping of items that have been clicked but I dont know how to do that. Need desperate help here.
I was able to overcome above problem after maintaining a boolean in the groupsData. Upon selection, I update a boolean "groupsJoined" in groupsData and update the state of groupsData which will invoke render. Inside GroupsItem class, I added a check that if data from props has joinedGroups as true then render selected state else non selected state.
Courtesy https://hackernoon.com/how-to-highlight-and-multi-select-items-in-a-flatlist-component-react-native-1ca416dec4bc

How do I flex horizontally within a column layout? Currently only half the width of the screen

How do I flex horizontally within a column layout? Currently only half the width of the screen
Here's the current render code:
render() {
const {handle_data} = this.state
if (handle_data.length) {
return (
<ScrollView style={styles.container}>
{
handle_data.map(pack => {
console.log('this.cleanDataForFlatlist(pack): ', this.cleanDataForFlatlist(pack))
return (
<View style={styles.pack}>
<Text style={{fontWeight: 'bold'},{fontSize: 24}}>{pack[0].pack_name}</Text>
<Text style={{fontWeight: 'bold'},{fontSize: 16}}>{pack[0].pack_description}</Text>
<View style={styles.pack}>
<FlatList
data={this.cleanDataForFlatlist(pack)}
keyExtractor={this._keyExtractor}
renderItem={this._renderHandle}
/>
</View>
</View>
)
})
}
</ScrollView>
);
} else {
return null
}
}
Here's what it looks like now
I do not see your code about an item, so I show an example code. your problem is mainly the item render.
// the soruceData is the data souce,in other words, your handle_data
<View style={{flex:1, backgroundColor:'transparent'}} >
<FlatList
data={this.state.sourceData}
renderItem={this._renderItem}
/>
</View>
//the render item method, I suggest you put the item into a pure component
_renderItem = ({ item }) => {
return (
<View style={{flexDirection:'column',width:'92%'}>
id={item.id} // the every item should hava a unique id or key
<ImageView source={{uri:"image url"}} style={{width:,height:}}/>
<Text numberOfLines={1} style={{fontSize: 18,
color: '#353535',}}>"content"</Text>
/>
);
};
you set layout width or height should use flex or percent. in this case, your UI is flexible.
At the same time, I suggest you read felx layout guide
In the end, I suggest you remove the scroll view, move views which algin above the flatList into the flatList ListHeaderComponent. it can avoid the scroll conflict.

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

FlatListFooter can't be displayed

I want to add footer to my flatList :
i try this code :
renderFooter = () => {
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<Button> This is footer </Button>
</View>
);
}
<FlatList
data={menuData}
renderItem={({item}) => <DrawerItem navigation={this.props.navigation} screenName={item.screenName} icon={item.icon} name={item.name} key={item.key} />}
ListFooterComponent ={this.renderFooter}
/>
But no footer appears when running.
Any help please
You used the component
ListFooterComponent
in right way. you need to check your render method for footer. I faced the same issue and i follow this example, and it helps me. I hope it will help you.
Simple way/hack. In the menuData array, you just add a flag (i called) to the child object to indicate that it's a last item. For eg:
If you can modify your menuData structure, added lastItem prop true to indicate that it's the last item :
const menuData = [
{name:'menu1', screenName:'screen1', icon:'../assets/icon1.png'},
{name:'menu2', screenName:'screen2', icon:'../assets/icon2.png', lastItem:true}
];
and then
renderFlatItems = ({item}) => {
const itemView = null;
if (!items.lastItem) {
itemView = <DrawerItem navigation={this.props.navigation} screenName={item.screenName} icon={item.icon} name={item.name} key={item.key} />
} else {
itemView = <View style={{padding:100}}><Button> This is footer </Button> </View>
}
return {itemView};
}
then use it in the Flatlist like so
<FlatList
data={menuData}
renderItem={this.renderFlatItems}
/>
If you want a footer that remains at the bottom of the screen "Above" the list, then you can just add a View after the FlatList.
<View>
<FlatList style={{flex: 1}} />
<View
style={{
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
height: 50,
}}
/>
</View>