React native FlatList with Radio Button - react-native

class App extends Component {
constructor() {
super();
this.state = {checked: false}}
onCheck = () => {
const { checked } = this.state;
if(checked == true){this.setState({ checked: false }) }
else {this.setState({ checked: true })}}
render() {
return (
<FlatList
data = {[
{firstName:'User_A',},
{firstName:'User_B',},
{firstName:'User_C',},
{firstName:'User_D',},
{firstName:'User_E',},
]}
renderItem = {({item}) =>
<TouchableOpacity onPress={() => { this.onCheck() }} activeOpacity = {0.5}>
<View style = {{flexDirection : 'row'}}>
<Left>
<Radio selected = {this.state.checked}/>
</Left>
<Card style = {{marginRight : 100, height : 50}}>
<View>
<View>
<Text> {item.firstName} </Text>
</View>
</Card>
</View>
</TouchableOpacity>
}
/>
)
}
}
Using react native i need a flatlist with radio button for selecting each item separately, but when i press an item every item in the list gets selected. How to manage single item selection? Above is my code and sample output

The idea here would be:
To create a separated component to avoid useless re-renders
To store the selected index in the state, and not a boolean, so that the radio button would look like
<Radio selected={this.state.selectedIndex === index}/>, where index is part of the object received by renderItem

Related

FlatList rerender all items favourite

I wanna achieve this effect: when i will press icon item will be added to favourite list and when i press again item will be deleted from favourite list but without rerender all of items from flatlist ( only rerender 1 item ). Now when i press icon all items from the list are rerender. Whats wrong? Items and list of favourite items come from api. They are two arrays of objects. I put id favourite items to array. When id of object is in array you can delete or add to favourite.
snippet of code:
const [fav, setFav] = useState([]); // array of favourites id
const SingleItem = React.memo(({ item, navigation }) => {
return(
<TouchableWithoutFeedback onPress={() => navigation.navigate("xxx", {
id: item.id,
})}>
<View style={styles.singleItemContainer}>
<View style={styles.imageContainer}>
<SliderBox images={images}
ImageComponent={ImageBackground}
ImageComponentStyle={{ width: '90%', marginTop: 10,}}
imageStyle={{borderRadius: 12}}
>
{ route.params?.isLogged &&
<View style={styles.heartIcon}>
<IconFontAwesome name={fav.includes(item.id) ? "paw" : "paw-outline"}
size={30}
color={fav.includes(item.id) ? "red" : "white"}
onPress={() => {
if(fav.includes(item.id)){ // is favourite and delete from favourite
deleteFavourite(item.id)
setFav(fav.filter(el => el != item.id))
}
else{ // add to favourite
addFavourite(item.id)
setFav([item.id, ...fav])
}
}}/>
</View>
}
</SliderBox>
</View>
</View>
</TouchableWithoutFeedback>
)});
const renderItem = ({ item }) => {
return(
<SingleItem
item={item}
navigation={navigation}
/>
);}
return (
<View style={styles.screen}>
<FlatList
data={data}
initialNumToRender={12}
renderItem={renderItem}
keyExtractor={(item) => item.id}
onEndReachedThreshold = {0.2}
onEndReached = {fetchData}
/>
</View>
);
};

How to use the modal in the list in react native (a specific Modal for each list item)?

I made a customized list component (in React Native) which shows touchable images with some description texts.
I need each images open a specific Modal; but I don't know how!! where & how I should code the Modal??
... here is my photo list component:
export class CustomGallery extends Component {
render() {
let {list} = this.props;
return (
<View style={styles.container}>
<FlatList
numColumns={4}
data={list}
renderItem={({ item}) => (
<View style={styles.views}>
<TouchableOpacity style={styles.touch} >
<ImageBackground
style={styles.img}
source={{ uri: item.photo }}
>
<Text style={styles.txt}>{item.name}</Text>
<Text style={styles.txt}>{item.key}</Text>
<Text style={styles.txt}>{item.describtion}</Text>
</ImageBackground>
</TouchableOpacity>
</View>
)}
/>
</View>
);
}
}
For Modal you can use modal from material-ui - https://material-ui.com/components/modal/
The Modal component renders its children node infront of a backdrop component. Simple and basic example would be like a confirmation message that pops up asking whether you surely want to delete particular information or not.
From your code I am guessing you want to display information regarding the image using modal when you click on the image.
Here I have added Modal component:
import React from 'react';
import Modal from '#material-ui/core/Modal';
export class CustomGallery extends Component {
constructor() {
super();
this.state = {
modalOpen: false,
snackOpen: false,
modalDeleteOpen: false,
};
}
handleModalOpen = () => {
this.setState({ modalOpen: true });
}
handleModalClose = () => {
this.setState({ modalOpen: false });
}
render() {
let {list} = this.props;
return (
<View style={styles.container}>
<FlatList
numColumns={4}
data={list}
renderItem={({ item}) => (
<View style={styles.views}>
<TouchableOpacity style={styles.touch} >
<ImageBackground
style={styles.img}
onClick={() => this.handleModalOpen()}
>
{ item.photo }
</ImageBackground>
<Modal
open={this.state.modalOpen}
onClose={this.handleModalClose}
closeAfterTransition
>
<Text style={styles.txt}>{item.name}</Text>
<Text style={styles.txt}>{item.key}</Text>
<Text style={styles.txt}>{item.describtion}</Text>
</Modal>
</TouchableOpacity>
</View>
)}
/>
</View>
);
}
}
I am not sure about how you set the image. But anyways below method is an example of opening modal with dynamic data.
import React, {useState} from "react";
import { Button, TouchableOpacity, FlatList, Modal, Text } from "react-native";
function App() {
const [value, setValue] = useState("");
const DATA = [
{
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
title: 'First Item',
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
title: 'Second Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'Third Item',
},
];
return (
<>
<FlatList
data={DATA}
renderItem={({item}) => (
<TouchableOpacity onPress={() => setValue(item.title)}>
<Text>{item.title}</Text>
</TouchableOpacity>
)}
/>
<Modal visible={value}>
<Text>{value}</Text>
<Button title="close" onPress={() => setValue("")} />
</Modal>
</>
)
}
export default App;

React-Native: Passing keyword in props for searching

I need a little help in a small issue. In my first screen, I have a flatlist which has some keywords. I want if I click on any keyword, I should be navigated to another screen with the prop of that keyword. I am doing like this inside the flatlist:
<FlatList
data={myRecentSearches}
renderItem={({ item, index }) => (
<TouchableOpacity
onPress={() => props.navigation.navigate("search", { searchValue: `${item.keyword}` })}>
<Text>{item.keyword}</Text>
</TouchableOpacity>
)}
In the next screen, I want the input field should already be initialized with that keyword.
const SearchScreen = (props) => {
const [searchValue, setSearchValue] = useState("");
return (
<View activeOpacity={0.5} style={styles.searchContainer}>
<TextInput
allowFontScaling={false}
value={searchValue} //I want this value to be already initialized/populated with the prop
returnKeyType="search"
autoFocus
/>
</View>
)
}
In your first screen have the SearchScreen like this:
return(
<SearchScreen keyWord={item.keyword}/>
)
And inside SearchScreen:
const SearchScreen = (props) => {
const [searchValue, setSearchValue] = useState(props.keyWord);
return (
<View activeOpacity={0.5} style={styles.searchContainer}>
<TextInput
allowFontScaling={false}
value={searchValue} //I want this value to be already initialized/populated with the prop
returnKeyType="search"
autoFocus
/>
</View>
)
}

How to display particular image by clicking one flatlist images in react native?

I have a flat list that returns multiple images. but I want to show an image on another view by clicking FlatList image. when I click on FlatList image that particular image will be shown in another view. how to do please suggest.
Here is my FlatList:
<FlatList
data={this.state.images}
renderItem={this.renderGalleryImage}
keyExtractor={(item, index) => index}
horizontal={true}
/>;
renderGalleryImage = ({ item }) => {
return <Image source={item} style={styles.moreImg} />;
};
and here is the view where I want to display
<View>
</View>
You can approach this problem in several ways; The easiest way is to wrap the renderGalleryImage return statement with a Touchable primitive and add an onPress event handler.
You can use the useState hook in tandem to save the selected Image from the FlatList.
Here's a sample code snippet.
const [selectedItem, setSelectedItem] = useState("");
renderGalleryImage = ({ item }) => {
const setImage = () => setSelectedItem(item);
return (
<TouchableWithoutFeedBack onPress={() => setSelectedItem(item)}>
<Image source={item} style={styles.moreImg} />;
</TouchableWithoutFeedBack>
);
};
return <View>{selectedItem && <Image source={{ uri: selectedItem }} />}</View>;
Note: If you're using class-based components, follow this approach:
export default class App extends Component {
state = {
selectedImage: "",
};
renderGalleryImage = ({ item }) => {
const setImage = () => this.setState({ selectedImage: item.image });
return (
<TouchableWithoutFeedback onPress={setImage} style={{ margin: 30 }}>
<Image
source={{ uri: item.image }}
style={{ width: 100, height: 100 }}
/>
</TouchableWithoutFeedback>
);
};
render() {
const renderImage = () => (
<View>
<Image
source={{ uri: this.state.selectedImage }}
style={{ width: 100, height: 100,borderColor:'red',borderWidth:1 }}
/>
</View>
);
return (
<View>
<FlatList data={users} renderItem={this.renderGalleryImage} />;
<Text>Selected Image</Text>
{renderImage()}
</View>
);
}
}
Here's the link to a working demo on Expo.

Handle Multiselect in a GridView

I'm trying to handle the multi-select with react-native-super-grid , here is my code :
<GridView
itemDimension={80}
items={items}
style={styles.gridView}
renderItem={item => (
<View style={[styles.itemContainer , { backgroundColor:' transparent '}]}>
<TouchableHighlight style={styles.buttonStyle} onPress={() => this.pressEvent() }>
<Text> {item.image}</Text>
</TouchableHighlight>
<Text style={styles.buttonText}> {item.name}</Text>
</View>)}
/>
I tried using this function :
pressEvent(arr){
if(this.state.pressStatus == false){
this.setState({ pressStatus: true})
this.state.arr.push(arr)
this.setState({ color : 'white'})
} else {
this.setState({ pressStatus: false})
this.setState({ color: 'red'})
}
}
but it somehow doesn't work , can someone help me ?
Thank you .
This short example should give you an idea what are you doing wrong. The items itself are not aware of the state. So what I would do, I would create a separate child component for grid item and handle press state locally. Then handle parent, which is holding all the item trough callback about the pressed item.
class MyGridView extends Component {
render() {
return (
<GridView
itemDimension={80}
items={items}
style={styles.gridView}
renderItem={item => (
<GridItem
item={item}
onItemPress={selected => {
// set grid view callback
if (selected) {
//if true add to array
this.addToPressedArray(item);
} else {
//false remove from array
this.removeFromPressedArray(item);
}
}}
/>
)}
/>
);
}
// You don't change the state directly, you mutate it trough set state
addToPressedArray = item => this.setState(prevState => ({ arr: [...prevState.arr, item] }));
removeFromPressedArray = item => {
const arr = this.state.arr.remove(item);
this.setState({ arr });
};
}
And the GridItem
class GridItem extends Component {
// starting local state
state = {
pressStatus: false,
color: 'red'
};
// handle on item press
pressEvent = () => {
this.setState(prevState => ({
pressStatus: !prevState.pressStatus, //negate previous on state value
color: !prevState.pressStatus ? 'white' : 'red' //choose corect collor based on pressedStatus
}));
// call parent callback to notify grid view of item select/deselect
this.props.onItemPress(this.state.pressStatus);
};
render() {
return (
<View style={[styles.itemContainer, { backgroundColor: ' transparent ' }]}>
<TouchableHighlight style={styles.buttonStyle} onPress={() => this.pressEvent()}>
<Text> {item.image}</Text>
</TouchableHighlight>
<Text style={styles.buttonText}> {item.name}</Text>
</View>
);
}
}
I also recommend to read about React.Component lifecycle. Its a good reading and gives you a better understanding how to achieve updates.
Since GridView has been merged into FlatGrid. Therefore, I've implemented the multi-select option in a pretty easy way. First of all I applied TouchableOpacity on top of the view in the renderItems prop of FlatGrid like this.
<TouchableOpacity
onPress={() => this.selectedServices(item.name)}>
...props
</TouchableOpacity>
SelectedServices:
selectedServices = item => {
let services = this.state.selectedServices;
if (services.includes(item) == false) {
services.push(item);
this.setState({ selectedServices: services });
} else {
let itemIndex = services.indexOf(item);
services.splice(itemIndex, 1);
this.setState({ selectedServices: services });
}
};
Using splice, indexOf, and push, you can easily implement multi-selection.
To change the backgroundColor of the currently selected item, you can apply a check on the backgroundColor prop of the view.
renderItem={({ item, index }) => (
<TouchableOpacity
onPress={() => this.selectedServices(item.name)}
>
<View
style={[
styles.itemContainer,
{
backgroundColor: this.state.selectedServices.includes(
item.name
)
? '#0052cc'
: item.code
}
]}
>
<Text style={styles.itemName}>{item.name}</Text>
</View>
</TouchableOpacity>
)}