I created a checkbox inside my flatlist but when I click on the checkbox all the check boxes will render. I want to render the checkbox I press not the entire checkboxes.
This is my code.
const ScreenCart = () => {
const [checked, setChecked] = useState(false)
const renderItem = ({ item, index }) => {
return (
<View style={styles.list}>
<View style={{ flexDirection: 'row' }}>
<CheckBox
checkBoxColor={COLORS.ORANGE}
isChecked={checked}
onClick={() => setChecked(!checked)}
/>
<Image
source={item.image}/>
<View>
<Text numberOfLines={1} style={[styles.item, { width: 210 * rate }]}>{item.name}
</Text>
</View>
</View>
</View>
)
}
return (
<View style={{ backgroundColor: COLORS.WHITE, flex: 1 }}>
<View style={{ flex: 1 }}>
<Flatlist
data={TEMP_DATA_CART}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}>
/>
</View>
)
this is my data.
TEMP_DATA_CART = [
{
id: '1', image: IMAGES.imgPromote, name: 'Sữa tắm Prunus - Premier Herb', value: 180000, quantity: 1, checked: false,
},
{
id: '2', image: IMAGES.imgPromote, name: 'Sữa tắm Prunus - Premier Herb', value: 180000, quantity: 1, checked: false,
},
{
id: '3', image: IMAGES.imgPromote, name: 'Sữa tắm Prunus - Premier Herb', value: 180000, quantity: 1, checked: false,
},
you set a global state so it is selecting checkbox for all items
remove this
const [checked, setChecked] = useState(false)
set array of selected id's
const [checked, setChecked] = useState([]);
on press set id in array and set check status from array
<CheckBox
checkBoxColor={COLORS.ORANGE}
isChecked={checked.includes(item.id)}
onClick={() => {
const newIds = [...checked];
const index = newIds.indexOf(item.id);
if (index > -1) {
newIds.splice(index, 1);
} else {
newIds.push(item.id)
}
setChecked(newIds)
}}
/>
and in flatlist set extraData prop
<Flatlist
data={TEMP_DATA_CART}
renderItem={renderItem}
extraData={checked}
keyExtractor={(item) => item.id.toString()}>
/>
to retrive selected id's you can get from checked array
Related
I have a question of understanding. I have different items, for each the user should be able to choose how much he wants. The articles are stored in Firebase with Img, Name, Category. How can the user now increase an item without an update being sent to the DB every time? Only when the user clicks a button should all quantities, in each case, be written to the database. The users are also stored in a DB with key and room.
Here is my code for the Food Articel:
{filter?.map((art, idx) => {
return <Food key={idx} art={art}></Food>;
})}
....
export default function Food({ art }) {
const [artikel, setArtikel] = useState(art);
const [anzahl, setAnzahl] = useState(0);
const updateAnzahl = (action, artName) => {
if (action === "remove") {
setArtikel((prevState) => ({
...prevState,
[artName]: {
anzahl: anzahl - 1,
},
}));
} else {
setArtikel((prevState) => ({
...prevState,
[artName]: {
anzahl: anzahl + 1,
},
}));
}
};
<IconButton
aria-label="delete"
onClick={() => updateAnzahl("remove", art.name)}
>
<RemoveIcon fontSize="large"></RemoveIcon>
</IconButton>
<Typography variant="h5">{anzahl}</Typography>
<IconButton
aria-label="delete"
onClick={() => updateAnzahl("add", art.name)}
>
<AddIcon fontSize="large"></AddIcon>
</IconButton>
import React, { useState } from 'react';
import {
SafeAreaView,
View,
Text,
FlatList,
StatusBar,
Button,
} from 'react-native';
const items = [
{
name: 'item 1',
qty: 0,
},
{
name: 'item 2',
qty: 0,
},
{
name: 'item 3',
qty: 0,
},
{
name: 'item 4',
qty: 0,
},
];
export default function App() {
const [data, setData] = useState(items);
const [refresh, setRefresh] = useState(''); // <- Add if your view not Rerender
const handleIncrease = (index) => {
const temp = data;
temp[index].qty = temp[index].qty + 1;
setData(temp);
setRefresh(Math.random()); // <- Add if your view not Rerender
};
const handleDecrease = (index) => {
const temp = data;
temp[index].qty = temp[index].qty - 1;
setData(temp);
setRefresh(Math.random()); // <- Add if your view not Rerender
};
const renderItem = ({ item, index }) => {
return (
<View
style={{
height: 50,
width: '90%',
marginLeft: '5%',
borderWidth: 1,
borderColor: 'black',
marginBottom: 10,
flexDirection: 'row',
}}>
<Text style={{ marginRight: 20 }}>{item.name}</Text>
<Button title="Increase" onPress={() => handleIncrease(index)} />
<Text style={{ marginHorizontal: 10 }}>{item.qty}</Text>
<Button title="Decrease" onPress={() => handleDecrease(index)} />
</View>
);
};
return (
<SafeAreaView style={{ flex: 1, marginTop: StatusBar.currentHeight }}>
<View style={{ flex: 1 }}>
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item, index) => String(index)}
/>
</View>
</SafeAreaView>
);
}
snack link: https://snack.expo.dev/9ZsvSkXBk
I'd keep the user's current order in browser's session/local storage and update database on some "submit" button. Piece of cake :-)
BTW. An issue to consider - what if user has more than one tab/browser opened on your app - should any instance simply overwrite others data or you need some syncing between them?
here's link to codesandbox with boilerplate for order management: https://codesandbox.io/s/hotel-cafe-order-f5wp7d
I am React Native Beginner and am using expo-radio-button.
But i am unable to select a single radio button at a time in flatlist.
if i select single button it will select only one button.
help me to fix this.
i need to show and select yes or no in the radio button for each questions
Click here to see output that i want
var myArray = [
{
isSignReq: "1",
sno: "1",
yesno: "0",
},
{
isSignReq: "1",
sno: "2",
yesno: "0",
},
{
isSignReq: "1",
sno: "3",
yesno: "0",
},
];
const [account, setAccount] = useState([]);
const setFormSubmit = (val) => {
console.log(val);
};
<FlatList
data={myArray}
keyExtractor={() => {
return (
new Date().getTime().toString() +
Math.floor(Math.random() * Math.floor(new Date().getTime())).toString()
);
}}
renderItem={(itemData) => {
return (
<View style={{ flexDirection: "row", marginVertical: "2%" }}>
<Text style={styles.data1}>{itemData.item.sno}</Text>
<Text style={styles.data2}>{itemData.item.name}</Text>
<View style={{ width: "30%", marginLeft: "1%" }}>
<RadioButtonGroup
onSelected={(value) => {
setFormSubmit(value), setAccount(value);
}}
selected={account}
size={22}
containerStyle={{ flexDirection: "row" }}
>
<RadioButtonItem label={"Yes"} value={itemData.item.isSignReq} />
<RadioButtonItem label={"No"} value={itemData.item.yesno} />
</RadioButtonGroup>
</View>
</View>
);
}}
/>
renderItem = ({item, index}) => {
.....
<RadioButton
innerColor={Colors.darkBlue}
outerColor={Colors.lightGray}
animation={'bounceIn'}enter code here
isSelected={this.state.selectedIndex === index}
onPress={() => {this.onPress(index)}}
/>
}
and replace onPress with
onPress = (index) => {
this.setState({ selectedIndex: index });
}
and update FlatList with extraData props as FlatList needs to be re-rendered as
<FlatList
keyExtractor={this._keyExtractor}
data={this.state.data}
renderItem={this.renderItem}
ItemSeparatorComponent={this.renderSeparator}
extraData={this.state}
/>
demo: https://snack.expo.dev/HkzA9cCSB
*I use the ref property in the flatlist component in react native, but I get an undefined is not an
object error. I looked at many places on
the internet but did not get a satisfactory answer. Please help me :)*
const DATA = [
{
id: 0,
title: "First Item",
},
{
id: 1,
title: "Second Item",
},
];
const [selectedId, setSelectedId] = useState(null);
**React.useEffect(() => {
this.el.scrollToIndex({ animated: true, index:selectedId});
}, []);**
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.item, style]}>
<Text style={styles.title}>{item.title}</Text>
</TouchableOpacity>
);
const renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "#6e3b6e" : "#f9c2ff";
return (
<Item
item={item}
onPress={() => setSelectedId(item.id)}
style={{ backgroundColor }}
/>
);
};
return (
<>
<FlatList
data={DATA}
horizontal={true}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
**ref={(el) => this.el = el}**strong text
/>
</>
)
}```
To use a ref, you need to use useRef (since you're on a function component). On function components you won't find yourself using this. When using useRef, you'll need to use .current to access the item itself. So, you can see I'm doing el.current.scrollToIndex
Also, you'll need to add selectedId as a dependency to your useEffect
export default function App() {
const DATA = [
{
id: 0,
title: "First Item",
},
{
id: 1,
title: "Second Item",
},
{
id: 2,
title: "Second Item",
},
{
id: 3,
title: "Second Item",
},
{
id: 4,
title: "Second Item",
},
{
id: 5,
title: "Second Item",
},
{
id: 6,
title: "Second Item",
},
];
const [selectedId, setSelectedId] = React.useState(null);
const el = React.useRef()
React.useEffect(() => {
el.current.scrollToIndex({ animated: true, index:selectedId});
}, [selectedId]);
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.item, style]}>
<Text style={styles.title}>{item.title}</Text>
</TouchableOpacity>
);
const renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "#6e3b6e" : "#f9c2ff";
return (
<Item
item={item}
onPress={() => setSelectedId(item.id)}
style={{ backgroundColor }}
/>
);
};
return (
<>
<FlatList
data={DATA}
horizontal={true}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
ref={el}
/>
</>
)
}
I have a list of category from calling external API and I am displaying it in a flatlist like this in horizontal scroll
but it is not changing state onPress , I want it should change color when user click on particular tab/button
my API json format data is like this
const data = [
{
id: '1',
title: 'General'
},
{
id: '2',
title: 'Student-Visa'
},
{
id: '3',
title: 'Study'
},
{
id: '4',
title: 'Festival'
},
{
id: '5',
title: 'NorthIndian-Food'
},
]
and I am using it as
const renderItem = ({ item, index }) => (
<View key={index} style={selectedCategory === item.title ? styles.activeCategory : styles.categoryContainer}>
<TouchableOpacity
activeOpacity={0.6}
underlayColor={COLORS.white}
onPress={()=>handleCategory(item.title)}
>
<Text>{item.title}</Text>
</TouchableOpacity>
</View>
);
return (
<List
data={data}
renderItem={renderItem}
horizontal={true}
style={styles.container}
contentContainerStyle={styles.contentContainer}
showsHorizontalScrollIndicator={false}
keyExtractor={item => item.id.toString()}
/>
)
const handleSelectedCategory = (title) => {
setSelectedCategory(title);
console.log(selectedCategory);
}
const [selectedCategory, setSelectedCategory] = useState();
I am using it as a separate component in another component
<FAQcategory selectedCategory={selectedCategory} />
any suggestion ? or help what I am doing wrong
Thanks in advance
This might help
const [selectedCategory, setSelectedCategory] = useState(0); // should be item index
const handleCategory = (index) => {
setSelectedCategory(index);
};
const renderItem = ({ item, index }) => (
<View
key={index}
style={
selectedCategory === index
? styles.activeCategory
: styles.categoryContainer
}
>
<TouchableOpacity
....
onPress={() => handleCategory(index)} // send index in param
>
....
</TouchableOpacity>
</View>
);
After getting data from API I set it to state, and render items in Flatlist,
when I select any item from it I manipulate data and add a new property to item object named as "toggle: true"
and it's works well when I select any item from list I add a border based on toggle,
But when I go back to previous screen then re open the lists screen I can see the border rendered around the items, although I reset the state when the unmounted screen
So what's the wrong I made here?
Code snippet
Data
export default {
...
services: [
{
id: 0,
name: 'nameS0',
logo:
'https://cdn2.iconfinder.com/data/icons/hotel-98/64/hair-dryer-tools-beauty-hairdressing-512.png',
price: 19.99,
},
],
employees: [
{
id: 0,
name: 'name0',
img:
'https://www.visualelementmedia.com/wp-content/uploads/2015/04/person-4-400x629.jpg',
},
...
],
};
const VendorProfile = ({navigation}) => {
const [services, setServices] = React.useState(null);
const [employees, setEmployees] = React.useState(null);
const [serviceSelected, setServiceSelected] = React.useState(null);
const [employeeSelected, setEmployeeSelected] = React.useState(null);
// For selected Item (services, employees)
const itemSelected = (data, id) => {
const updated = data.map((item) => {
item.toggle = false;
if (item.id === id) {
item.toggle = true;
data === services
? setServiceSelected(item)
: setEmployeeSelected(item);
}
return item;
});
data === services ? setServices(updated) : setEmployees(updated);
};
...
const renderEmployees = ({item}) => {
return (
<TouchableOpacity
onPress={() => itemSelected(employees, item.id)}
delayPressIn={0}
style={styles.employeeContainer}>
<EmployeePattern style={{alignSelf: 'center'}} />
<View style={styles.employeeLogo}>
<Image
source={{uri: item.img}}
style={[styles.imgStyle, {borderRadius: 25}]}
/>
</View>
<View style={{marginTop: 30}}>
<Text style={{textAlign: 'center'}}> {item.name}</Text>
</View>
<View style={{marginTop: 10, alignSelf: 'center'}}>
{item.toggle && <AntDesign name="check" size={25} color="#000" />} // here it's stuck after back and reopen the screen
</View>
</TouchableOpacity>
);
};
React.useEffect(() => {
setServices(VendorProfileData.services);
setEmployees(VendorProfileData.employees);
() => {
setServices(null);
setEmployees(null);
};
}, []);
return (
<View style={styles.container}>
<FlatList
data={services}
renderItem={renderServices}
horizontal
keyExtractor={(item) => item.id.toString()}
contentContainerStyle={{
justifyContent: 'space-between',
flexGrow: 1,
}}
/>
.....
</View>
);
};
Ok so after trying multiple times, i got it
change this
const updated = data.map((item) => {
to this
const updated = data.map((old) => {
let item = {...old};
and please make sure everything is working and we didn't break a thing :),
On your ItemSelected function you are passing the whole employees list, and going through it now thats fine, but when you changing one item inside this list without "recreating it" the reference to that item is still the same "because its an object" meaning that we are modifying the original item, and since we are doing so, the item keeps its old reference, best way to avoid that is to recreate the object,
hope this gives you an idea.