How to handle onpress for FlatList? - javascript

I have a grid of pokemons (below).
I am using flatlist to render the pokemons using the data fetched from an api. When I click on a pokemon I want to display the next page (below)
//Function for FlatList - To render Pokemon images
const renderPokemon = ({ item }) => {
let url = item.url
const idPokemon = url.split('https://pokeapi.co/api/v2/pokemon/')
const link = urlImage + idPokemon[1].substring(0, idPokemon[1].length-1) + ".png"
return (
//Individual images
<View style={styles.pokemons}>
<Image
style={styles.image}
resizeMode='contain'
source={{uri:link}}
/>
<Text style={styles.text}>{item.name}</Text>
</View>
)
}
//App container
<NavigationContainer>
<View style={styles.container}>
<TopBar/>
{/**Pokemon image grid display*/}
<FlatList
numColumns={2}
data={pokemons}
renderItem={renderPokemon}
keyExtractor={pokemon => `key-${pokemon.name}`}
style={styles.container}
onPress={() => alert('clicked')} //WHERE DO I PUT THIS ?
>
</FlatList>
</View>
<NavigationContainer>
But it doesnt seem to be responding. I am using Alert just to test. How to I get the flatlist imaages to handlle onpress so it navigates to the next page and displays the data for that specific pokemon?
Thanks in advance!

You need to add Pressable inside each rendered item, after importing from react-native and add onPress event and do as required in the callback function.(like in your case, navigate to new screen). You can pass pokemonDetailsObj in route params or just the pokemonId, depending on how the Pokemon details Component works.
I would also suggest you to create stack navigator and register your screen components to better use [reactnavigation][1] features.
import { Pressable} from 'react-native';
const renderPokemon = ({ item }) => {
let url = item.url
const idPokemon = url.split('https://pokeapi.co/api/v2/pokemon/')
const link = urlImage + idPokemon[1].substring(0, idPokemon[1].length-1) + ".png"
return (
//Individual images
<Pressable
onPress={() => {
// pass the 'ScreenName' of the Pokemon details component.
props.navigation.navigate('ScreenName', {
// props to be passed to the component like pokemonId or full pokemon Details Obj
})
}}
>
<Image
style={styles.image}
resizeMode='contain'
source={{uri:link}}
/>
<Text style={styles.text}>{item.name}</Text>
</Pressable>
)
}
[1]: https://reactnavigation.org/docs/getting-started

Related

Remove an item in AsyncStorage using FlatList

Sorry for the inexperience, but how do I remove an Item in Async Storage renderized in Flat List, for example:
This is my component that creates a flatlist
export default function Saved() {
const [colors, setColors] = useState([]);
useEffect(() => {
async function getStorage() {
const nomeStorage = await AsyncStorage.getAllKeys();
if (nomeStorage != null) {
setColors(nomeStorage);
}
}
getStorage();
}, [colors])
return (
<View style={styles.body}>
<FlatList
data={colors}
keyExtractor={(item) => item}
renderItem={({ item }) => <Saveds data={item} />}
/>
</View>
);
}
and this is my FlatList renderized component
export default function Saveds(props) {
return (
<View>
<View style={styles.Boxes}>
<Box color={props.data}>
<Badge
badgeStyle={styles.badge}
value={<Text style={styles.color}>{props.data}</Text>}
/>
</Box>
<TouchableOpacity style={styles.btn}>
<Icon name={'trash-outline'} color={'#FFF'} size={30} />
</TouchableOpacity>
</View>
</View>
);
}
I need one way to when I click in my TouchableOpacity, I delete the selected data in my AsyncStorage.
The name in my AsyncStorage is the same as the value, so I can delete the AsyncStorage getting the value of my props.data.
Anyone can help me?
Deleting from your async storage should be as easy as just calling AsyncStorage.removeItem(key)
I had some similar functionality in an app that I made a while ago, I attached the delete function call to the onLongPress prop of touchableOpacity:
<TouchableOpacity
onPress={() => navigation.navigate('UserScreen', data)}
onLongPress={handleLongPress}>
<View style={styles.container}>
// ...
</View>
</TouchableOpacity>
And earlier in the component, I defined a function that handles the deleting:
const handleLongPress = async () => {
// In your instance, you should be OK to replace data.key with props.data
await AsyncStorage.removeItem(data.key);
/* I then had another function that was passed in as a prop
to update another component when the deletion had taken place */
await onChange();
};

How to pass Button component's title into a function in React Native

I want to pass the title of a React Native Button component into a neighbouring function. I am using React Native functional components only for this application.
Here's the component. I would like to pass the title of the button pressed by the user, which will be either 'English' or 'Arabic', into the function submitLanguageSelection so that I can then save that value into useLocalStorage(), a custom hook I wrote to handle AsyncStorage, so that the next time the user uses the app, their language choice will be persisted, and they will not be shown the ChooseYourLanguageScreen again.
All help appreciated, thank you.
const ChooseYourLanguageScreen = ({ navigation }) => {
const [saveData, storedValue, errorMessage] = useLocalStorage();
const [userSelectedLanguage, setUserSelectedLanguage] = React.useState('');
const submitLanguageSelection = () => {
//TODO: receive params from onPress
//TODO: save the data locally
//TODO: navigate to welcome screen
};
return (
<View style={styles.container}>
{errorMessage ? <Text>{errorMessage}</Text> : null}
<Text style={styles.text}>This is the Choose Your Language Screen</Text>
<View style={styles.buttons}>
<View>
<Button
title={'English'}
onPress={() => submitLanguageSelection()}
/>
</View>
<View>
<Button title={'Arabic'} onPress={() => submitLanguageSelection()} />
</View>
</View>
</View>
);
};
You can simply pass it to the function
<Button title={'Arabic'} onPress={() => submitLanguageSelection('Arabic')} />
And access like below
const submitLanguageSelection = (language) => {
console.log(language);
};
Getting data from a sibling component is an anti-pattern.
The source of the knowledge of the language options is the ChooseYourLanguageScreen component (as seems from your snippet), so it should hold the list of available languages. Having that, you can just iterate through them and render the appropriate components:
<View style={styles.buttons}>
{languages.map((language) => (
<View key={language}>
<Button
title={language}
onPress={() => submitLanguageSelection(language)}
/>
</View>
))}
</View>

update setState instance in react native

I am trying to fetch images stored on firebase storage and display in react native app. the functions works to get the download URL of images but it doesn't merge previous state and update component, it just displays in only one image (due to useState). so as each image is fetched it displays the image in the same image component instead of creating a new component to display in
Code:
import React, {setState, useState} from 'react';
import {DrawerActions, useNavigation} from '#react-navigation/native';
const Gallery = () => {
const navigation = useNavigation();
const [downloadUrl, setDownloadUrl] = useState({url: undefined});
console.log(`URL should be undefined at this point ${downloadUrl}`);
function listFilesAndDirectories(reference, pageToken) {
return reference.list({pageToken}).then(result => {
result.items.forEach(ref => {
ref.getDownloadURL().then(url => {
// console.log(`Image URL is:\n ${url}`);
setDownloadUrl({url: url});
});
});
if (result.nextPageToken) {
return listFilesAndDirectories(reference, result.nextPageToken);
}
return Promise.resolve();
});
}
const storageReference = firebase
.storage()
.refFromURL('gs://ab404.appspot.com/images');
listFilesAndDirectories(storageReference).then(() => {
console.log('Started listing image download urls');
});
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<View style={SharedStyles.header}>
<TouchableOpacity
onPress={() => {
navigation.dispatch(DrawerActions.openDrawer());
}}>
<Hamburg />
</TouchableOpacity>
<Header title="Gallery" />
</View>
<ScrollView contentInsetAdjustmentBehavior="automatic">
<View style={styles.container}>
<View style={styles.sectionContainer}>
<View>
<Text>
Welcome to the Abulad Gallery! Come back for more content....
</Text>
<Image
source={{uri: downloadUrl.url}}
style={styles.fetchedImage}
/>
</View>
</View>
</View>
</ScrollView>
</SafeAreaView>
</>
);
};
I have tried using the spread operator to get previous state and update as needed but I get an invalid attempt to spread a non-iterable instance
I need a way to make images appear in their component while updating url
Thanks

Using a ref in a FlatList in React Native

I am still having trouble understanding ref's in React Native (and React in general). I am using functional component. I have a FlatList that has many items. How do I create a reference for a thing within an item like a Text or View component?
<FlatList
data={data}
renderItem={({ item }} => {
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => _editITem(item.id)}>
<Text ref={(a) => 'text' + item.id = a}>EDIT</Text>
</TouchableOpacity>
</View>
}
/>
Then in _editItem I want to reference the Text component so that I can change its text from 'EDIT' to 'EDITING', or even change its style, or whatever.
_editPost = id => {
console.log(text + id)
}
I have tried...
FeedComponent = () => {
let editPosts = {}
<FlatList
data={data}
renderItem={({ item }} => {
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => _editITem(item.id)}>
<Text ref={(a) => editPosts[item.id] = a}>EDIT</Text>
</TouchableOpacity>
</View>
}
/>
...and a few other things, but I think I might be way off so I could use some guidance.
Typically you don't use refs in react to update content like text. Content should be rendered based on the current props and state of your component.
In the case you describe you'll probably want to set some state in the parent component that then impacts the rendering of the item.
As a sidenote refs are used if you need to trigger a method on a child component like calling focus on a TextInput for example but not for imperatively updating component content.
In your case you'll want to update some state representing the current active item. Something like:
import React, {useState} from 'react';
FeedComponent = () => {
const [activeItem, setActiveItem] = useState(null);
<FlatList
data={data}
renderItem={({ item }} => {
return (
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => setActiveItem(item.id)}>
{activeItem === item.id
? <Text>EDITING</Text>
: <Text>EDIT</Text>
}
</TouchableOpacity>
</View>
);
}
extraData={activeItem}
/>

Unable to loop and render FlatList inside .map function react-native

I am trying to render a FlatList inside a component. The Component itself is inside a ScrollView.
I am using map function to loop through the data to pass into the component.
Earlier I was using ScrollView instead of FlatList. It was working fine, but was rendering slow. So I decided to use FlatList.
Here's my code:
renderComp(){
const { filtersView,cats,cats_title, clearStyle} = styles;
const data = this.props.ingreds;
const arr = Object.entries(data);
return arr.map(i=> {
const name= i[0];
const items_obj = i[1];
const items = Object.values(items_obj);
return(
<View key={name} style= {filtersView}>
<View style={cats}>
<Text style ={cats_title}>{name}</Text>
<Text style={clearStyle}>Clear All</Text>
</View>
<View style={{justifyContent:'flex-start', alignItems:'flex-start'}}>
<FlatList
style={{ marginRight:6}}
data={items}
keyExtractor={(x,i)=> i.toString()}
renderItem={({item}) =>{
this.renderItems(item)
}}
/>
</View>
</View>
)
})
}
And here's the ScrollView Component:
<ScrollView contentContainerStyle={{alignItems:'flex-start',
justifyContent:'flex-start',flex:1, height:72}} >
{this.renderComp()}
</ScrollView>
And The loop stops after one iteration.
Here's the output: https://i.stack.imgur.com/yM151.png
Any suggestions?
ReactNative FlatList renderItem method should return a ?React.Element component. In your case either use return this.renderItems or skip the inner brackets.
https://facebook.github.io/react-native/docs/flatlist#renderitem
({item}) => this.renderItems(item)}

Categories