Hide picked items in a FlatList - reactjs

I'm using react-native FlatList to render out a punch on elements the data of which comes from an external API.
I want to prevent the user from picking the same item twice. So the way I'm going about this it hiding the item after they pick it.
If, let's say, I have a state picked like so
const [ picked, setPicked ] = useState(false);
changing it will of course hide all the elements.
<FlatList
{/*some other props*/}
data={allCards}
renderItem={(card: ListRenderItemInfo<CardsProps>) => (
<TouchableOpacity
style={[ styles.holder, { display: picked ? "none" : "flex" } ]}
onPress={() => handleChoice(parseInt(card.item.id))}
>
<Card
{/*some card props*/}
/>
</TouchableOpacity>
)}
/>
How can I go about changing the state for only one element inside the FlatList??
Is there a better approach you would recommend to do the same job?

try it like this, it will work,
<FlatList
{/*some other props*/}
data={allCards}
renderItem={(card: ListRenderItemInfo<CardsProps>) => picked ? (
<TouchableOpacity
style={[ styles.holder, ]}
onPress={() => handleChoice(parseInt(card.item.id))}
>
<Card
{/*some card props*/}
/>
</TouchableOpacity>
): undefined}
/>

The easiest way to do this is if you create an object with all the ids and while inserting it you can check it whether it's in that list or not
it'll cost u O(1) time to check.
<FlatList
{/*some other props*/}
data={allCards}
renderItem={(card: ListRenderItemInfo<CardsProps>) => picked ? (
<TouchableOpacity
style={[ styles.holder, ]}
onPress={() => handleChoice(parseInt(card.item.id))}
>
<Card
{/*some card props*/}
/>
</TouchableOpacity>
): undefined}
/>
const map=new Map();
const handleChoice=(id)=>{
if(Map.get(id)===undefined){
Map.set(id,true)
//add the element to ur list where u need it to
}
}

Related

How do I tap only once on touchable opacity placed inside a scrollview?

I already researched similar questions on the topic, so please don't be in a hurry to close this question.
I have a search screen, where I type a search term and I fetch listed results. Once fetched, I want to tap on any one of them and navigate to that page details object. My initial first tap is never been detected, but my second is. My component:
return (
<View>
<TextInput
value={query}
placeholder="Type Here..."
onChangeText={(search) => setQuery(search)}
/>
{fetching ? (
<Spinner
visible={fetching}
textContent={'Fetching data...'}
textStyle={{ fontSize: 14 }}
/>
) : null}
{items ? (
<ScrollView keyboardShouldPersistTaps={true}>
<FlatList
numColumns={1}
horizontal={false}
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() =>
navigation.navigate('Item Details', { id: item.id })
}>
<ItemDisplay item={item} keyboardShouldPersistTaps="always" />
</TouchableOpacity>
)}
/>
</ScrollView>
) : null}
</View>
);
My ItemDisplay is just a row of item's photo and item's id. Nothing fancy.
As suggested on the similar questions, I do use <ScrollView keyboardShouldPersistTaps={true}> and <ItemDisplay item={item} keyboardShouldPersistTaps="always" /> However it doesn't help m, I still need to tap twice. Any ideas on how do I fix this? I am testing it on android, if that matters.
I also tried <ScrollView keyboardDismissMode="on-drag" keyboardShouldPersistTaps={"always"} > but this doesnt help as well.
If I understood your problem correctly, you are facing an issue clicking an item when the Keyboard is present.
If so you can simply pass keyboardShouldPersistTaps as always to your FlatList and then the children of the list will receive taps when an item is pressed.
Also, you shouldn't be using a ScrollView to wrap your Flatlist, if they are both having the same orientation, numColumns isn't required as you have passed it as 1
You can read more about keyboardShouldPersistTaps at https://reactnative.dev/docs/0.64/scrollview#keyboardshouldpersisttaps
Take a look at this Live Snack to see it in action.

Implementing a counter in a React Native Flat List

Background
I'm building a screen for an app which displays a number of products brought from a firebase firestore collection. The way I display the products is via a FlatList, this is the code:
<FlatList
style={selected ? styles.visible : styles.hidden}
data={data}
renderItem={({ item }) => (
<View style={styles.container}>
<View style={styles.left}>
<Text style={styles.title}>{item.name}</Text>
<Text style={styles.description}>{item.description}</Text>
<Text style={styles.price}>${item.price}</Text>
<View style={styles.adder}>
<TouchableOpacity onPress={() => setCount(count - 1)}>
<Text style={styles.less}>-</Text>
</TouchableOpacity>
<Text style={styles.counter}>{count}</Text>
<TouchableOpacity onPress={() => setCount(count + 1)}>
<Text style={styles.more}>+</Text>
</TouchableOpacity>
</View>
</View>
<Image style={styles.right} source={{uri: item.image}}/>
</View>
)}
/>
Problem
As you can see, there's a counter in place for each item which the user can use to select how many units of the product they want to buy. The state is updated like this: const [count, setCount] = useState(0);
As you can see, this way of implementing the counter means that adding one unit of any product adds a unit of every product.
What I have tried
I've seen some people do this: setCount({counterA: 0, counterB: 0, etc.}) and update each counter, however as the number of products is always changing in the firebase document I can't hardcode the amount of counters I need.
Question
How can I edit my code so that it only affects the desired item?
The item in your renderItem function should be a separate component. In this separate component you will have a count that is scoped only to the component. So when the button is pressed on that component, only its count is incremented and not the others. The problem right now is you have one count that is on the parent component. After creating this new "counting" componenet, your FlatList will look something like:
<FlatList
style={selected ? styles.visible : styles.hidden}
data={data}
renderItem={({ item }) => (
<CountingComponent />
)}
/>

Adjacent JSX elements

I will be very happy if you can help me. I am making react native expo app. I have code that displays information from the database. When I inserted the code to add an image, I had an error, why?
Error:
Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...? (79:12)
<View>
<ListView
dataSource={this.state.dataSource}
renderSeparator= {this.ListViewItemSeparator}
renderRow={(rowData) =>
<Image
source={require('../assets/rose-blue-flower-rose-blooms-67636.jpeg')}
/>
<Text
onPress={() => {
/* 1. Navigate to the Details route with params */
this.props.navigation.navigate('Details', {
otherParam: rowData.article_title,
});
}}
>{rowData.article_title}</Text>
}
/>
</View>
<View>
<ListView
dataSource={this.state.dataSource}
renderSeparator={this.ListViewItemSeparator}
renderRow={rowData => (
<>
<Image
source={require("../assets/rose-blue-flower-rose-blooms-67636.jpeg")}
/>
<Text
onPress={() => {
/* 1. Navigate to the Details route with params */
this.props.navigation.navigate("Details", {
otherParam: rowData.article_title,
});
}}
>
{rowData.article_title}
</Text>
</>
)}
/>
</View>;
This issue is that you're rendering in your expression two components that are on the same level. so you must have a JSX fragment or another div as a single parent in your return statement of the renderRow.. I believe the above code should solve the problem
You just need to wrap the elements with a Fragment element. The reason why is ListView requires one element to work off of for a "row". You can simulate this with the fragment :)
<React.Fragment>
<Image />
<Text />
</React.Fragment>
theres some nice syntax for fragments if your compiler supports it
<>
</>
To apply it to your code
<ListView
dataSource={this.state.dataSource}
renderSeparator={this.ListViewItemSeparator}
renderRow={rowData => (
<React.Fragment>
<Image
source={require("../assets/rose-blue-flower-rose-blooms-67636.jpeg")}
/>
<Text
onPress={() => {
/* 1. Navigate to the Details route with params */
this.props.navigation.navigate("Details", {
otherParam: rowData.article_title,
});
}}
>
{rowData.article_title}
</Text>
</ React.Fragment>
)}
/>

Changing Visibility of a button inside the component dynamically

I am using React-Navigation but I guess You don't really need to have a prior knowledge about it.
So I have a header Component which looks like this
const Header = (props) => {
return (
<View style={headerContainer}>
<View>
<Button onPress={() => props.navigation.navigate('Home')}
title="Go Back"/>
</View>
<Text style={header}> Knicx
<Text style={headerSecondary}> Crypto Ticker </Text>
</Text>
</View>
)
}
Now, In the above notice the button, Which I am currently showing on all the pages
<Button onPress={() => props.navigation.navigate('Home')}
title="Go Back"/>
But I don't want it to be there on some specific pages, like HomeScreen to say the least.
Now, One solution is to remove the <Header /> component in my homepage, Copy the above code snippet, past it in my HomeScreen and remove the Component ( sort of like hardcoding ) or two create two Header component, one with button and one without button
[Question:] But I was thinking if we could toggle the visibility of button dynamically or stack it on the top of <Header /> ? Can someone please tell me how can we do it? (No, We can set the opacity of the button to zero and change it whenever we need it)
[Update:] We can use redux to manage/pass the state but the problem is that in React-native we do not have display: none and I don't want to change use the opacity
Send showHomeButton: boolean as a prop to header
const Header = props => (
<View style={headerContainer}>
{this.props.showHomeButton && (
<View>
<Button onPress={() => props.navigation.navigate('Home')} title="Go Back" />
</View>
)}
<Text style={header}>
{' '}
Knicx
<Text style={headerSecondary}> Crypto Ticker </Text>
</Text>
</View>
);

react-native-elements: The last few items in the Transaction List cannot be viewed at bottom

I am using react-native-elements to speed up the development of my react-native app.
I am using react-native ScrollView in my List element to enable scroll functionality.
Unfortunately, it seems that the List element is overflowing at the bottom, hiding the last few list item elements from view
Any idea why this is the case?
EDIT: Here is the render function of the list component
render () {
const { transactions, handleEditTransaction, handleDeleteTransaction } = this.props
return (
<View>
<View>
<List>
<ScrollView>
{
transactions.map((item, i) => (
<TransactionListItem
key={i}
item={item}
/>
))
}
</ScrollView>
</List>
</View>
</View>
)
}
I had the same problem. To solve this, go to the root <View> and add style={{flex: 1}}.
This means to also add this to the parent component if necessary.
<View style={{flex:1}}>
<ScrollView>
//Content here
</ScrollView>
</View>
parent component (if required)...
<View style={{flex:1}}>
<ChildComponent />
</View>

Resources