Checked - Unchecked doesn't working in ListView - React Native - reactjs

friend I Will integrated checked - unchecked in listView. So that When user click on checked then store the data in Array and unchecked then i will remove the data. Its working fine, But the UI Will not updated after checked - unchecked.
<List containerStyle={{marginTop : 0 , borderBottomWidth : 0 , borderBottomColor : 'black', borderTopWidth : 0}}>
<FlatList
data={this.state.list}
renderItem={({ item }) => (
<ListItem containerStyle={{height: 80, backgroundColor : 'transparent', borderBottomWidth : 0, borderTopWidth : 0}}
title={
<View style={styles.titleView}>
<Text style={styles.ratingText}>{item.iWorkerID.vFirstName}</Text>
</View>
}
rightIcon={
<TouchableOpacity onPress = {() => this.selectedWorker(item)} style={{width: 30, height: 30 , marginTop : 10, marginRight : 30}}>
<Image style = {{width: 30, height: 30}} source={this.state.selectedList.includes(item) ? require("./Images/uncheckd.png") : require("./Images/checked.png")}/>
{/* {this.state.selectedList.includes(item) && <Image style = {{width: 30, height: 30}} source={require("./Images/uncheckd.png")}/>}
{!this.state.selectedList.includes(item) && <Image style = {{width: 30, height: 30}} source={require("./Images/checked.png")}/>} */}
</TouchableOpacity>
}
avatar={<Avatar
rounded
medium
containerStyle={{marginLeft: 30}}
source={{uri: Globle.IMAGE_URL+item.vProfileImage}}
activeOpacity={0.7}
/>}
/>
)}
/>
</List>
And on the check/uncheck button, I will add/remove object from array,
selectedWorker = (data) =>{
console.log('data is ', data);
if (!this.state.selectedList.includes(data)) {
// this.setState({ selectedList : [...this.state.selectedList , data]})
this.state.selectedList.push(data);
} else {
var index = this.state.selectedList.indexOf(data);
if (index > -1) {
this.state.selectedList.splice(index, 1);
}
}
this.setState({list : this.state.list})
console.log('selected list' , this.state.selectedList);
}
Main Issue : Want to update image checked/unchecked according to selectedList array, How can i Update item in listView.
What to do inside selectedWorker method.
GIF :

you are using Flatelist inside List, Both are a component to listing items. you can use List or Flatelist, not both.
I hope it will help you..
I try to make Demo similar to that you want.
constructor(props) {
super(props)
this.state = {
list: [
{
id: 1,
name: "Harpal Singh Jadeja",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
},
{
id: 2,
name: "Kirit Mode",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
},
{
id: 3,
name: "Rajiv Patil",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
},
{
id: 4,
name: "Chetan Doctor",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
}]
};
};
renderListItem = (index, item) => {
return (
<View style={styles.notification_listContainer}>
<Image source={{uri: item.avtar, cache: 'force-cache'}}
style={circleStyle}/>
<View style={{flex: 1, paddingLeft: 10, justifyContent: 'center'}}>
<Label roboto_medium
align='left'
color={Color.TEXT_PRIMARY}
numberOfLines={1}>
{item.name}
</Label>
<Label roboto_medium
small
align='left'
color={Color.TEXT_SECONDARY}
mt={8}>
Programmer
</Label>
</View>
<View style={{justifyContent: 'center'}}>
<TouchableHighlight
style={{
backgroundColor: item.isSelected ? Color.BLACK : Color.TEXT_SECONDARY,
alignItems: 'center',
justifyContent: 'center',
height: 40,
width: 40,
borderRadius: 20
}}
onPress={this.onSelectWorker.bind(this, index, item)} underlayColor={Color.BLACK}>
<Icon name='done'
size={20}
color={Color.WHITE}/>
</TouchableHighlight>
</View>
</View>
);
};
onSelectWorker = (index, item) => {
console.log("Selected index : ", index);
let tempList = this.state.list;
tempList[index].isSelected = tempList[index].isSelected ? false : true
this.setState({
list: tempList
});
};
render() {
return (
<View style={styles.notification_Container}>
<FlatList
data={this.state.list}
renderItem={
({index, item}) => this.renderListItem(index, item)
}
keyExtractor={item => item.id}
extraData={this.state}
/>
</View>
)
}

You need to add a key to your ListItem which is based on a unique id of the item, so that React can distinguish between the items rendered.
When you use index of an array as a key, React will optimize and not render properly. What happens in such a scenario can be explained with an example.
Suppose React renders an array of 10 items and renders 10 components. Suppose the 5th item is then removed. On the next render React will receive an array of 9 items and so React will render 9 components. This will show up as the 10th component getting removed, instead of the 5th, because React has no way of differentiating between the items based on index.
Therefore always use a unique identifier as a key for components that are rendered from an array of items.

Related

React Native : Error in the data part of Flatlist

I tried using Flatlist in order to respond to one of my issue but I got an error on the data= of my Flatlist . I don't rreally undersstand the issue and the error message is not helping ( No overload matches this call. Overload 1 of 2, '(props: FlatListProps<any> | Readonly<FlatListProps<any>>): FlatList<any>', gave the following error. Type '{ id: string; name: string; data: string[]; description: string[]; }' is missing the following properties from type 'readonly any[]': length, concat, join, slice, and 19 more. )
I used flatlist because of this : React Native : Conditional Views.
Here is my code:
<View style={{ flex: 10}}>
{letter.map((letter) => {
const isleetergotdata = letter.data?.length > 0;
if (!isleetergotdata) {
return null;
}
return (
<View
style={{
flex: 1,
flexDirection: "row",
}}
>
<div
ref={fieldRef}
style={{
marginBottom: 100,
}}
>
<Text
style={{
fontSize: 90,
}}
>
{letter.name}
</Text>
</div>
{letter.data?.map((item, index) => {
if (total !== same) {
return (
<FlatList
data={letter} // HERE IS THE ISSUE
numColumns={2}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item }) => {
return (
<View>
<Text>{letter.description[index]}</Text>
</View>
);
}}
/>
);
}
})}
</View>
);
})}
</View>
And here is my data:
const letter = [
{
id: "1",
name: "A",
data: ["Abricot", "Abricot", "Abricot"],
description: [
"Un abricot est un fruit",
"Un abricot est un fruit",
"Un abricot est un fruit",
],
},
//...
{
id: "5",
name: "E",
data: [],
description: [],
},
//...
];
The problem here is that you are providing letter to the FlatList's data prop, but this is not the global letter variable but the one of the local scope of the outer map function. You have given them the same name. Thus, letter in the map function is an object and not an array.
It seems to me that you want to create a FlatList for each object inside the "global" letter variable and provide the description property of each of these objects (which is an array and you are acessing it via the index which is really not necessary).
Thus, you actually only need to use one map and remove the inner map function. I would also rename the local variable letter to something else.
<View style={{ flex: 10}}>
{letter.map((datum) => {
const isleetergotdata = datum.data?.length > 0;
if (!isleetergotdata) {
return null;
}
return (
<View
style={{
flex: 1,
flexDirection: "row",
}}
>
<div
ref={fieldRef}
style={{
marginBottom: 100,
}}
>
<Text
style={{
fontSize: 90,
}}
>
{datum.name}
</Text>
</div>
if (total !== same) {
return (
<FlatList
data={datum.description}
numColumns={2}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item }) => {
return (
<View>
<Text>{item}</Text>
</View>
);
}}
/>
);
}
)
}
</View>
);
})}
</View>
And to be precise here, remove the letter.data?.map((item, index) part completely.
If you need both data and desription in the same FlatList, then you can still leave the inner map function.
<FlatList
data={datum.data}
numColumns={2}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item, index }) => {
return (
<View>
<Text>{datum.description[index]}</Text>
</View>
);
}}
/>
You can get the data from array.
Step:01 Inside API call.
jsonElement["Letter"]
Step:02
const [dataSource, setDataSource] = useState([]);
Step:03
<FlatList horizontal={true} data={dataSource} />
Step:04
{item.id}
₹ {item.name}
When I run your code I face a different error related to the div tag. React-Native has no dom and if you do not have a custom 'div' component, you can not use the div tag. Even if a 'Div' component is in your project, component names should start with a capital letter.

React-native FlatList rendering issue

THE PROBLEM
see the screenshot of probleme
//below code is which loades the slider..you can also find the below snippet in ClassFlatListRender.js , when id of element passed through FlatList which equals to the selected Id then the audio slider should work, but if I trigger one audio file element all of the sliders are moving
{id == this.state.selectedOptionId ?
(<View style={[styles.viewBar, { flexDirection: 'row', }]}>
<View style={[styles.viewBarPlay, { width: playWidthMessage }]} />
</View)
:
<View><Text>no slider<Text></View>}
THE CODE
The structure of my code is this: I have a container component with all the logic and state,
which contains a FlatList component , which again contains a custom presentational List.
Container
Custom list component that includes the FlatList component and the renderItem method
invokes classFlatList component (presentational, stateless)
The container includes this component
<CustomList
items={this.state.data}
/>
CustomList:
export class CustomList extends Component {
render() {
const renderItem = ({ item }) => {
return (
<ClassFlatListRender
message={item.message}
id={item.id}
user={item.user}
timestamp={item.timestamp}
heights={item.heights}
url={item.url}
audioLength={item.audioLength}
type={item.type}
/>
)
}
let { items } = this.props
return (
<View style={styles.container}>
< FlatList
style={styles.container}
data={items.slice().sort((a, b) => b.timestamp - a.timestamp)}
// data={data}
renderItem={renderItem}
inverted={true}
extraData={items}
keyExtractor={(items) => `${items.timestamp}`}
/>
</View>
)
}
}
The ClassFlatListRender.js
export class ClassFlatListRender extends React.Component {
constructor() {
super();
this.state = {
update: false,
currentPositionSec: 0,
currentDurationSec: 0,
playTime: '00:00:00',
duration: '00:00:00',
id: '',
tempId: '',
selectedOptionId: '',
setId: '',
};
this.audioRecorderPlayer = new AudioRecorderPlayer();
this.audioRecorderPlayer.setSubscriptionDuration(0.09); // optional. Default is 0.1
}
onStartPlay = async (id, url) => {
console.log('onStartPlay');
//? Custom path
// const msg = await this.audioRecorderPlayer.startPlayer(this.path);
//? Default path
console.log("arvinf", url)
const msg = await this.audioRecorderPlayer.startPlayer(url);
console.log("msg", msg)
const volume = await this.audioRecorderPlayer.setVolume(1.0);
console.log(`file: ${msg}`, `volume: ${volume}`);
this.audioRecorderPlayer.addPlayBackListener((e) => {
this.setState({
currentPositionSec: e.currentPosition,
currentDurationSec: e.duration,
// playWidth: (e.currentPosition / e.duration) * (screenWidth - 300)
// playTime: this.audioRecorderPlayer.mmssss(
// Math.floor(e.currentPosition),
// ),
// duration: this.audioRecorderPlayer.mmssss(Math.floor(e.duration)),
});
});
};
onPausePlay = async () => {
await this.audioRecorderPlayer.pausePlayer();
};
renderElement = (id, audioLength) => {
if (this.state.selectedOptionId == id) {
// this.onStartPlay(url)
return (
<Fcon name={this.state.selectedOptionId == id ? 'play' : 'pasue'} size={24} color={'white'} />
)
}
if (this.state.selectedOptionId !== id || this.state.currentPositionSec == this.state.duration) {
// this.onPausePlay(url)
return (
<Fcon name={'play'} size={24} color={'white'} />
)
}
}
checkFunction = async (id, url) => {
console.log("id: before", id)
await this.setState({
selectedOptionId: id,
setId: id
})
console.log('id:after', this.state.selectedOptionId)
id == this.state.setId ? this.onStartPlay(id, url) : this.onPausePlay()
// return this.state.selectedOptionId
}
render() {
let { id, user, url, heights, message, audioLength, type } = this.props;
let playWidthMessage =
(this.state.currentPositionSec / this.state.currentDurationSec) *
(screenWidth - 300);
if (!playWidthMessage) {
playWidthMessage = 0;
}
return (
<View id={id}>
{
url.endsWith(".jpg") ?
< View key={id} style={[user == 1 ? styles.receiver : styles.sender, { paddingHorizontal: 14, paddingVertical: 8, borderRadius: heights > 50 ? 20 : 50 }]}>
<View style={{ alignItems: 'center', }}>
{console.log("address", url)}
<Image
resizeMode="contain"
// resizeMethod="scale"
style={{ width: 200, height: 200 }}
source={{ uri: url, width: 200, height: 200 }}
// source={{ uri: "https://picsum.photos/200", width: 200, height: 200 }}
// source={require("https://ibb.co/hM8BbY5")}
/>
</View>
</View> : null
}
{
url.endsWith(".mp3") ?
(
<View key={this.props.id} style={[user == 1 ? styles.receiver : styles.sender, { paddingHorizontal: 14, paddingVertical: 8, borderRadius: heights > 50 ? 20 : 50 }]}>
<View style={[styles.viewPlayer, { backgroundColor: user == 1 ? darkgrey : '#0096FF', alignSelf: 'center', borderRadius: 50, flexDirection: 'row', justifyContent: "center", alignItems: 'center' }]}>
{/* line */}
<TouchableOpacity activeOpacity={0.5} onPress={() => {
this.checkFunction(id, url)
}} style={{ borderRadius: 50, width: 36, height: 36, justifyContent: 'center', alignItems: 'center' }}>
<View>
{this.renderElement(id, url, audioLength)}
</View>
</TouchableOpacity>
<View >
<TouchableOpacity
style={[styles.viewBarWrapper, { width: screenWidth - 300 }]}
onPress={this.onStatusPress}
>
{id == this.state.setId ?
(<View style={[styles.viewBar, { flexDirection: 'row', }]}>
{/* {console.log("playWidth:", this.state.playWidth)} */}
<View style={[styles.viewBarPlay, { width: playWidthMessage }]} />
</View>
)
: <View><Text>ddd</Text></View>}
</TouchableOpacity>
</View>
<Text style={[styles.txtRecordCounter, { color: 'white' }]}>{audioLength}</Text>
</View>
</View >) : null
}
{
type == "txt" ?
// && user == 1 ?
<View key={id} >
<TouchableOpacity onPress={async () => await this.setState({ tempId: id })} style={[styles.receiver, { borderRadius: heights > 50 ? 20 : 50, backgroundColor: this.state.tempId == id ? 'pink' : 'yellow' }]}>
<Text style={styles.receiverText}>{message}</Text>
</TouchableOpacity>
</View> : null
}
</View >
)
}
}
Maintain the selected audio id in your parent component, pass it to child components using props, animate based on the condition. in ur message component

Dealing With Multiple Flatlists on the same Screen

Please help my deal with unnecessary re-renders when having two flatlists on the same screen
My screen requires two flatlists-
For HOSTS
For QUEUE
When the component mounts, I get data from the api call like this-
{
"hosts": [{"id":"1", "name":"kyouma"},...],
"queue": [{"id":"99", "name":"eren"},...]
}
Now what I do is I store my hosts and queue separately in my redux store like this-
this.props.dispatch({
type: GET_ROOM_HOSTS,
payload: info['hosts']
})
this.props.dispatch({
type: GET_ROOM_QUEUE,
payload: info['queue']
})
where info is the object received from the api call as shown above. Now I mapStateToProps these two from the redux store to the default screen component such that-
this.props.roomQueue is for queue and
this.props.roomHosts is for hosts
My FlatList's are like this-
<FlatList
data={this.props.roomQueue}
horizontal={true}
keyExtractor = {item => item.id}
renderItem({item} => {
return(
<customComponent (with suitable props) ..../>
)
})
/>
<FlatList
data={this.props.roomHosts}
numColumns={3}
keyExtractor = {item => item.id}
renderItem({item} => {
return(
<customComponent (with suitable props) ..../>
)
})
/>
PLEASE NOTE that both the FlatList's are present in the same Component (React.Component) of the screen and are displayed at different parts of the screen(queue at bottom of the screen and hosts at the top of the screen). Also queue and hosts are independent of each other. Screen looks like this
My problem is that even if there is a change in this.props.roomQueue, the FlatList having its data={this.props.roomHosts} get's re-rendered.
How do i prevent this re-render to ensure that only if the FlatList's corresponding data changes, then only will it re-render, otherwise it won't. Do I have to change the way I store queue and hosts? or is there something else?
You can do this with using only one flatlist. Merge your both array's into one and show results from one list.. you can spare them in ui with a type.
This is a genuine procedure of what developers do, cz rendering 2 list in same page and same direction is accually no mean. Your query is valid.
You can add List ListFooterComponent and it will automatically do this for you
<FlatList
contentContainerStyle={{
width: WINDOW_WIDTH,
paddingVertical:WINDOW_WIDTH*0.2,
marginLeft:10
}}
ListFooterComponent={()=> returnYourViewDesignHere()}
columnWrapperStyle={{ flex: 1, justifyContent: "space-around" }}
keyExtractor={(item) => item.id}
onEndReached={() => getPaginationData()}
onEndReachedThreshold={0.0001}
numColumns={3}
showsVerticalScrollIndicator={false}
data={allShows}
renderItem={({ item, index }) => {
return (
<TouchableWithoutFeedback
key={item.index + Math.floor(Math.random() * 1000)}
onPress={() =>
props.navigation.navigate(
item.type == "movie" ? "MovieDetailScreen" : "SeasonDetail",
{
data: item,
object: {
id: item.id,
},
}
)
}
>
<View style={styles.boxContainer}>
<View style={styles.imageBackground}>
<Text style={styles.backgroundText}>KEIN</Text>
<Text
style={[styles.backgroundText, { color: COLOR.primary }]}
>
POSTER
</Text>
</View>
<Image
source={{
uri: item.coverUrl ? item.coverUrl : item.coverPath,
}}
style={styles.imageBox}
resizeMode={"stretch"}
/>
<Text
numberOfLines={2}
ellipsizeMode={"tail"}
style={styles.text}
>
{item.showTitle ? item.showTitle : item.title}
</Text>
{userWatchedList.some((uwl) => uwl.id == item.id) ? (
<TouchableWithoutFeedback
onPress={() =>
isloggedIn
? removeFromUserWatchList(item)
: handleModalVisibility()
}
>
<Image
source={WATCHLIST_CHECKED}
style={{
width: 25,
height: 25,
position: "absolute",
right: 5,
top: 5,
}}
/>
</TouchableWithoutFeedback>
) : (
<TouchableWithoutFeedback
onPress={() =>
isloggedIn
? addToUserWatchList(item)
: handleModalVisibility()
}
>
<Image
source={CIRCLE_UNCHECKED}
style={{
width: 25,
height: 25,
position: "absolute",
right: 5,
top: 5,
}}
/>
</TouchableWithoutFeedback>
)}
</View>
</TouchableWithoutFeedback>
);
}}
/>

API image not being displayed in list

I know there are several questions with this issue but mine is different.
I trying to display an image in my Flatlist card that is coming from an API. However it is not showing up.
BUT...when I display this image in another part of my code (in an Autocomplete list) using the same code basically, it works. Also, when I try an url from an image on the Web, it displays inside the flatlist correctly
Here's my Flatlist code:
<FlatList
data={this.state.myGamesArray}
renderItem={({ item }) => (
<Card>
<CardItem>
<View>
<Image
style={styles.gameImage}
source={{uri: item.background_image}}
/>
</View>
</CardItem>
<CardItem>
<View>
<Text style={styles.usergameText}>
{item}
</Text>
</View>
</CardItem>
</Card>
)}
keyExtractor={(item,index) => index.toString()}
/>
Here is my Autocomplete code in which I use the same image bracket-thingy
<View style={styles.iconContainer} >
<TouchableOpacity onPress={() => this.setState({ query: item.name})}
style={styles.autocompleteList} >
<View>
<Image
style={styles.gameImage}
source={{uri: item.background_image}}
/>
</View>
<Text style={styles.gameText}>
{item.name}
</Text>
</TouchableOpacity>
</View>
I ran console.item(item.background_image) both inside the Flatlist(first snippet) and the Autocomplete list (Second snippet). The first shows 'undefined' and the second it shows all the URIs
App.js full code:
/*This is an example of AutoComplete Input/ AutoSuggestion Input*/
import React, { Component } from 'react';
//import react in our code.
import { StyleSheet, Text, TouchableOpacity, View, Image, FlatList, Alert, TouchableWithoutFeedback, Keyboard } from 'react-native';
//import all the components we are going to use.
import Autocomplete from 'react-native-autocomplete-input';
import { Button, List, Container, ListItem, Card, CardItem, Header, Item } from 'native-base';
import { Entypo } from '#expo/vector-icons'
//import Autocomplete component
//const API = 'https://api.rawg.io/api/games?page=1';
//Demo base API to get the data for the Autocomplete suggestion
class App extends Component {
constructor(props) {
super(props);
//Initialization of state
//films will contain the array of suggestion
//query will have the input from the autocomplete input
this.state = {
myGamesArray: [],
games: [],
query: ' ',
};
}
componentDidMount() {
//First method to be called after components mount
//fetch the data from the server for the suggestion
fetch('https://api.rawg.io/api/games?page=1&platforms=18', {
"method": "GET",
"headers": {
"x-rapidapi-host": "rawg-video-games-database.p.rapidapi.com",
"x-rapidapi-key": "495a18eab9msh50938d62f12fc40p1a3b83jsnac8ffeb4469f"
}
})
.then(res => res.json())
.then(json => {
const { results: games } = json;
this.setState({ games });
//setting the data in the games state
});
}
findGame(query) {
let i;
//method called everytime when we change the value of the input
if (query === '') {
//if the query is null then return blank
return [];
}
const { games } = this.state;
//making a case insensitive regular expression to get similar value from the film json
const regex = new RegExp(`${query.trim()}`, 'i');
//return the filtered game array according the query from the input
return games.filter(game => game.name.search(regex) >= 0);
}
AddItemsToArray = () => {
var i
//verifica se input esta vazio
if (this.state.query === '') {
return Alert.alert('Voce não selecionou um jogo')
}
//VERIFY IF GAME IS IN THE ARRAY
for (i = 0; i < this.state.games.length - 1; i++) {
if (this.state.query !== this.state.games[i].name) {
if (i === this.state.games.length - 2) {
return Alert.alert('Este jogo nao existe')
}
else {
continue
}
} else {
break
}
}
//verifica repetido
if (this.state.myGamesArray.includes(this.state.query)) {
return Alert.alert('Este jogo já foi adicionado')
}
else {
//Adding Items To Array.
this.setState(prevState => {
const { myGamesArray, query } = prevState;
return {
myGamesArray: [...myGamesArray, query.toString()],
};
},
// Use setState callback to alert with the updated state
);
}
}
render() {
const { query } = this.state;
const games = this.findGame(query);
const comp = (a, b) => a.toLowerCase().trim() === b.toLowerCase().trim();
return (
<TouchableWithoutFeedback
onPress={() => {
Keyboard.dismiss()
}}
>
<View style={styles.container}>
<View style={styles.listContainer}
>
<FlatList
data={this.state.myGamesArray}
renderItem={({ item }) => (
console.log(item.background_image),
<Card style={{flexDirection:'row',paddingEnd:100}}>
<CardItem cardBody>
<View>
<Image
style={styles.listImage}
source={{uri: item.background_image}}
/>
</View>
</CardItem>
<CardItem>
<View>
<Text style={styles.usergameText}>
{item}
</Text>
<Text style={styles.usergameText}>
Playstation 4
</Text>
</View>
</CardItem>
</Card>
)}
keyExtractor={(item,index) => index.toString()}
/>
</View>
<View>
<Header span
style={styles.header}>
<Autocomplete
inputContainerStyle={{borderColor:'transparent'}}
style={styles.autocompleteInput}
autoCapitalize="none"
autoCorrect={false}
//data to show in suggestion
data={games.length === 1 && comp(query, games[0].name) ? [] : games}
//default value if you want to set something in input
defaultValue={query}
/*onchange of the text changing the state of the query which will trigger
the findGame method to show the suggestions*/
onChangeText={text => this.setState({ query: text })}
placeholder=" Adicione os jogos que você tem"
//This below is the 'list' of autocomplete options
renderItem={({ item }) => (
//you can change the view you want to show in suggestion from here
//I GET ERROR WHEN TRYING TO ERASE (PS4) IN TEXT BOX ***NEED TO CHECK THIS
<View style={styles.iconContainer} >
<TouchableOpacity onPress={() => this.setState({ query: item.name})}
style={styles.autocompleteList} >
<View>
<Image
style={styles.gameImage}
source={{uri: item.background_image}}
/>
</View>
<Text style={styles.gameText}>
`${item.name}`
</Text>
</TouchableOpacity>
</View>
)}
/>
</Header>
</View>
<TouchableOpacity
style={styles.addButton}
onPress={() => this.AddItemsToArray()}
>
<Entypo name="plus" size={50} color="#fff" />
</TouchableOpacity>
</View>
</TouchableWithoutFeedback>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
flex: 1,
},
autocompleteInput: {
borderWidth: 1,
backgroundColor: "#fff",
borderColor: '#7843FF',
height: 50,
marginTop: 70,
borderRadius:10,
},
autocompleteList: {
flex:1,
flexDirection: 'row',
borderWidth:0.5,
borderColor: '#7843FF',
paddingVertical: 5,
paddingRight: 60,
},
listContainer: {
flex: 1,
position: 'absolute',
left: 10,
right: 10,
top:150,
flexDirection:'column',
justifyContent: 'center',
borderColor: '#7843FF',
},
gameText: {
fontSize: 15,
marginLeft: 10,
marginRight:30,
marginVertical:10,
color: '#000',
textAlign: 'left',
justifyContent: 'center'
},
usergameText: {
fontSize:15,
textAlign: 'left',
alignSelf:'stretch',
color: '#000',
},
gameImage: {
flex: 3,
height: 60,
width: 60,
marginLeft:10,
borderRadius: 100,
},
listImage: {
flex: 3,
height: 110,
width: 90,
marginLeft:0,
},
addButton: {
height:50,
width: 50,
position: 'absolute',
left: 371,
top: 71,
backgroundColor: '#7843FF',
borderTopRightRadius: 10,
borderBottomRightRadius:10,
},
usergameImage: {
height: 100,
width: 100,
borderRadius: 100,
},
header: {
backgroundColor:'#67E6DC'
}
});
export default App;

How to keep track of a group of mapped components in React Native?

I have a group of text buttons that are rendered by mapping data:
<View style={{paddingTop: '3%', height:"100%", width:1400, flexDirection: "row", flexWrap: 'wrap'}}>
{this.props.data.map((item, index) => {
console.log("profession item: ", item)
return (
<View
style={{margin:4, marginHorizontal:12}}
key={item.pro_id}>
<TextItemToggle
value={item.en}
onPress={(value)=>{this._onPressHandler(value)}}
/>
</View>
)
})}
</View>
Only one of the button can be highlighted. Once another button is pressed and highlighted, the one that is already highlighted will be toggled. I'm not sure how to keep track of components rendered through mapping.
I first thought of using string (each button has a unique string value) to reference each button, but it is likely to be deprecated according to https://hashnode.com/post/why-are-string-refs-considered-legacy-in-react-cit1ttd8x0spevx53ltk1gdhh Are there any other approaches?
One option can be to store the id of the item that needs to be highlighted in state and while mapping checking that id to highlight.
Example
_onPressHandler = (event, id) => {
this.setState({ selected: id});
}
render() {
return (
<View style={{paddingTop: '3%', height:"100%", width:1400, flexDirection: "row", flexWrap: 'wrap'}}>
{this.props.data.map((item, index) => {
console.log("profession item: ", item)
return (
<View
style={[{margin:4, marginHorizontal:12}, (this.state.selected === item.pro_id : {backgroundColor: 'grey'} : {})]}
key={item.pro_id}>
<TextItemToggle
value={item.en}
onPress={(value)=>{this._onPressHandler(value, item.pro_id)}}
/>
</View>
)
})}
</View>
)
}

Resources