React native flatlist multiple selection is not updating - javascript

I am using a flatlist to expose all the list of ingredients I have saved on my database, but once I update the state, it is not reflecting on the flatlist component.
The flatlist component
<FlatList
horizontal
bounces={false}
key={ingredientList.id}
data={ingredientList}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => selectedIngredient(item)}>
<Card key={item.id}>
<Text key={item.title} style={styles.titleText}>{item.name}</Text>
<Text key={item.title} style={styles.titleText}>{item.isSelected?'selected':'not selected'}
</Text>
<ImageBackground key={item.illustration} source={item.illustration} style={styles.cardImage}>
</ImageBackground>
</Card>
</TouchableOpacity>
)}
keyExtractor={(item) => item.index}
/>
This is the function that find the selected item "selectedIngredient"
function selectedIngredient(item) {
console.log('received: ', item.name)
item.isSelected = !item.isSelected;
return { ...item.isSelected }
}
That component call is working when I try debbug with console.log after the "item.isSelected = !item.isSelected", but the IU is not updated. Can someone help me to understand how to fix it?

You need to set state by using either setState in a class-based component or either using useState hook in the functional component.
function selectedIngredient(item) {
console.log('received: ', item.name)
item.isSelected = !item.isSelected; //this is not how you set state
return { ...item.isSelected }
}
Screenshot:
here is our old example which I modified for your current scenario:
import React, { useState } from 'react';
import {
Text,
View,
StyleSheet,
FlatList,
TouchableOpacity,
} from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
// or any pure javascript modules available in npm
const ingredientList = [
{
id: 1,
name: 'item1',
selected: false,
},
{
id: 2,
name: 'item 2',
selected: false,
},
{
id: 3,
name: 'item 3',
selected: false,
},
{
id: 8,
name: 'item 4',
selected: false,
},
{
id: 4,
name: 'item 5',
selected: false,
},
{
id: 5,
name: 'item 6',
selected: false,
},
];
export default function App() {
const [selectedItem, setSelectedItem] = useState(null);
const [allItems, setAllItems] = useState(ingredientList);
const selectedIngredient = (item) => {
console.log('selecionado: ' + item.name);
setSelectedItem(item);
/* Below operation can be improved by passing index to the function itself.
so filtering would not be required
*/
let temp = allItems.filter((parentItem) => parentItem.id !== item.id);
item.selected = !item.selected;
temp = temp.concat(item);
temp.sort((a, b) => parseInt(a.id) - parseInt(b.id));
setAllItems(temp);
console.log(allItems);
};
return (
<View style={styles.container}>
<FlatList
style={styles.flatlist}
horizontal
bounces={false}
data={allItems}
renderItem={({ item }) => (
<TouchableOpacity
style={styles.flatListItem}
key={item.id}
onPress={() => selectedIngredient(item)}>
<Text>{item.name}</Text>
{!item.selected ? (
<Text style={{ color: 'red' }}>{'Not Selected'}</Text>
) : (
<Text style={{ color: 'green' }}>{'Selected'}</Text>
)}
</TouchableOpacity>
)}
keyExtractor={(item) => item.index}
/>
{selectedItem ? (
<View style={styles.selectedTextView}>
<Text style={styles.selectedText}>{`${selectedItem.name} ${
selectedItem.selected ? 'selected' : 'not selected'
}`}</Text>
</View>
) : (
<View style={styles.selectedTextView}>
<Text style={styles.selectedText}>{`Nothing selected`}</Text>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
flatListItem: {
width: 100,
height: 100,
backgroundColor: 'white',
margin: 5,
borderRadius: 10,
justifyContent: 'center',
alignItems: 'center',
},
selectedTextView: {
flex: 8,
backgroundColor: 'white',
margin: 5,
borderRadius: 10,
justifyContent: 'center',
alignItems: 'center',
fontSize: 20,
},
selectedText: {
fontSize: 30,
},
});
Live Demo

Related

Add and Remove items from state array with React Native Checkbox

I am trying to add and remove items from a state array using React Hooks and React Native Checkboxes. I am able to add the value of the checkbox to the array when checked, but I am unable to remove the value from the array at all, let alone when the checkbox is un checked. I have tried many different filter solutions, and do not want to use splice to avoid mutation. For now I am trying to use a separate button to delete the value from the array. Here is an expo snack https://snack.expo.dev/jN_QvE7pb
import React, {useState, useEffect, useRef} from 'react';
import {Text, View, StyleSheet, FlatList, Button, Modal, CheckBox} from 'react-native';
// import CheckBox from '#react-native-community/checkbox';
import {Card} from 'react-native-paper';
const data = [
{id: 1, txt: 'item1', isChecked: false},
{id: 2, txt: 'item2', isChecked: false},
{id: 3, txt: 'item3', isChecked: false},
{id: 4, txt: 'item4', isChecked: false},
];
const Example = () => {
const [products, setProducts] = useState(data);
const [arr, setArr] = useState([]);
useEffect(() => {
console.log(arr);
}, [arr]);
const addToArray = id => {
console.log(id);
products.map(product => {
if (id === product.id) {
setArr(prev => [...prev, product.id]);
}
});
};
const removeFromArray = id => {
setArr(arr.filter((e, i) => e.id !== id));
};
return (
<View style={styles.container}>
<FlatList
data={products}
renderItem={({item}) => (
<Card style={{margin: 5}}>
<View style={styles.card}>
<View
style={{
flexDirection: 'row',
flex: 1,
justifyContent: 'space-between',
}}>
<CheckBox
value={item.isChecked}
onChange={() => {
addToArray(item.id);
}}
/>
<Button
onPress={() => {
removeFromArray(item.id);
}}
title="Remove From Array"
/>
<Text>{item.txt}</Text>
</View>
</View>
</Card>
)}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
card: {
padding: 10,
margin: 5,
flexDirection: 'row',
justifyContent: 'space-between',
},
modalView: {
margin: 20,
backgroundColor: 'white',
borderRadius: 20,
padding: 5,
justifyContent: 'space-between',
alignItems: 'center',
elevation: 5,
},
text: {
textAlign: 'center',
fontWeight: 'bold',
},
});
export default Example;

Why sorting of the Flatlist is not appearing? in react native

I am currently making sorting order in Ascending and Descending but the flatlist is not appearing.
I want to show sorting order by recently added, low to high & high to low by each separate buttons.
please help.
below is my code:
const [ demoList, setdemoList ] = useState([
{id: 3, name: 'Omkar'},
{id: 2, name: 'Abhishek'},
{id: 1, name: 'Saurabh'},
{id: 1, name: 'Saurabh'},
{id: 4, name: 'Chintan'},
{id: 6, name: 'Hardik'},
{id: 5, name: 'Lalit'},
]);
const sortListById = () => {
demoList.sort(function (obj1, obj2) {
return obj1.id - obj2.id;
});
};
return (
<View style={styles.container}>
<View style={{ margin: 10,alignItems: 'center',justifyContent: 'center',}}>
<FlatList data={demoList}
extraData={setdemoList}
renderItem={({ item }) => (
<View style={{ flexDirection: 'row' }}>
<Text style={{ fontSize: 20, margin: 15 }}>{item.id}</Text>
<Text style={{ fontSize: 20, margin: 15 }}>{item.name}</Text>
</View>
)}
keyExtractor={(item, index) => index}
/>
<View style={{ flex: 1 }}>
<Button title="Sort List" onPress={sortListById} />
</View>
</View>
</View>
);
full code is here: https://snack.expo.dev/#john.ocean/16c137
Working Example: Expo Snack, here you can sort by both ASC and DEC order.
Output:
Solution:
import React, { useState } from 'react';
import { StyleSheet, Text, View, FlatList, Button } from 'react-native';
let data = [
{ id: 3, name: 'Omkar' },
{ id: 2, name: 'Abhishek' },
{ id: 1, name: 'Saurabh' },
{ id: 1, name: 'Saurabh' },
{ id: 4, name: 'Chintan' },
{ id: 6, name: 'Hardik' },
{ id: 5, name: 'Lalit' },
];
export default function Home() {
const [demoList, setdemoList] = useState([...data]);
const [order, setOrder] = useState(1);
const sortListASC = () => {
demoList.sort((obj1, obj2) => {
return obj1.id - obj2.id;
});
setdemoList([...demoList]); // update
};
const sortListDES = () => {
demoList.sort((obj1, obj2) => {
return obj2.id - obj1.id;
});
setdemoList([...demoList]);
};
return (
<View style={styles.container}>
<View
style={{ margin: 10, alignItems: 'center', justifyContent: 'center' }}>
<FlatList
data={demoList}
renderItem={({ item }) => (
<View style={{ flexDirection: 'row' }}>
<Text style={{ fontSize: 20, margin: 15 }}>{item.id}</Text>
<Text style={{ fontSize: 20, margin: 15 }}>{item.name}</Text>
</View>
)}
keyExtractor={(item, index) => index}
/>
<View style={{ flex: 1, flexDirection: 'row' }}>
<View style={{ marginLeft: 10 }}>
<Button title={'ASC'} onPress={sortListASC} />
</View>
<View style={{ marginLeft: 10 }}>
<Button title={'DEC'} onPress={sortListDES} />
</View>
<View style={{ marginLeft: 10 }}>
<Button title={'Default'} onPress={() => setdemoList([...data])} />
</View>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
Use this to avoid data mutation.
const sortListById = () => setdemoList([...demoList].sort( (obj1, obj2)=>obj1.id - obj2.id))
If you use this
demoList.sort((obj1, obj2) =>{
return obj1.id - obj2.id;
});
this will change the value of demoList

How to update object Array State and screen style on touchable item click in react native

I am a bit new to react native and I am having difficulty updating object Array State and screen style on button item click during run time in react native
I tried using useState but the style does not change at run time on the touchable item click
Please advise me on the right way to change the screens view style on item click
see my code below
import React, {useState} from 'react';
import {ScrollView, FlatList, View, Text, TouchableOpacity} from 'react-native';
import styles from './styles';
export default function Loans({}) {
const [selectedDurationId, setSelectedDurationId] = useState(1);
const [selectedAmountId, setSelectedAmountId] = useState(1);
const changeSelectedDuration = function (id) {
console.log('before selected Duration');
console.log(id);
//change all the selecteds to no
durationArray.forEach(function (arrayItem) {
var x = (arrayItem.selected = 'no');
console.log(x);
setSelectedDurationId(id);
updateDataArray(durationArray, selectedDurationId);
console.log('after selected Duration');
});
};
const changeSelectedAmount = function (id) {
console.log('before selected Amount');
console.log(id);
//change all the selecteds to no
loanAmountArray.forEach(function (arrayItem) {
var x = (arrayItem.selected = 'no');
console.log(x);
});
setSelectedAmountId(id);
updateDataArray(loanAmountArray, selectedAmountId);
console.log('after selected Amount');
};
const updateDataArray = function (array, id) {
array.forEach(function () {
//change selected to yes where id == id
if (array.id === id) {
var x = (array.selected = 'yes');
console.log(x);
}
});
};
const loanAmountArray = [
{
id: 1,
amount: '5,000',
selected: 'yes',
},
{
id: 2,
amount: '10,000',
selected: 'no',
},
{
id: 3,
amount: '20,000',
selected: 'no',
},
];
const durationArray = [
{
id: 1,
days: '30 days',
rate: '3.3% Interest',
selected: 'yes',
},
{
id: 2,
days: '60 days',
rate: '5% Interest',
selected: 'no',
},
{
id: 3,
days: '90 days',
rate: '7% Interest',
selected: 'no',
},
];
return (
<View style={{flex: 1}}>
<ScrollView>
<View style={styles.contain}>
<Text>Chose Loan Amount</Text>
<FlatList
numColumns={6}
data={loanAmountArray}
keyExtractor={(item, index) => {
console.log('index', index);
return index.toString();
}}
renderItem={({item}) => {
console.log('item', item);
return (
<View>
<TouchableOpacity
onPress={() => {
changeSelectedAmount(item.id);
}}>
<Text
style={
item.selected === 'yes'
? styles.textBoxSelected
: styles.textBox
}>
{item.amount}
</Text>
</TouchableOpacity>
</View>
);
}}
/>
<Text>Chose Payment Duration</Text>
<FlatList
numColumns={3}
data={durationArray}
keyExtractor={(item, index) => {
console.log('index', index);
return index.toString();
}}
renderItem={({item}) => {
console.log('item', item);
return (
<View>
<TouchableOpacity
style={
item.selected === 'yes'
? styles.durationViewPressed
: styles.durationView
}
onPress={() => {
changeSelectedDuration(item.id);
}}>
<View>
<Text style={styles.interest}>{item.rate}</Text>
</View>
<View>
<Text style={styles.days}>{item.days}</Text>
</View>
</TouchableOpacity>
</View>
);
}}
/>
</View>
</ScrollView>
</View>
);
}
here's the style below
import {StyleSheet} from 'react-native';
export default StyleSheet.create({
textBox: {
marginTop: 13,
marginBottom: 30,
paddingTop: 10,
paddingLeft: 16,
paddingRight: 6,
fontSize: 18,
borderColor: '#1a2856',
borderWidth: 5,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
borderBottomLeftRadius: 20,
borderBottomRightRadius: 20,
},
textBoxSelected: {
marginTop: 13,
marginBottom: 30,
paddingTop: 10,
paddingLeft: 16,
paddingRight: 6,
fontSize: 18,
backgroundColor: '#1a2856',
color: '#fff',
borderWidth: 5,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
borderBottomLeftRadius: 20,
borderBottomRightRadius: 20,
},
durationView: {
marginTop: 10,
marginBottom: 20,
paddingLeft: 5,
paddingRight: 5,
},
durationViewPressed: {
marginTop: 10,
marginBottom: 20,
paddingLeft: 5,
paddingRight: 5,
borderColor: '#1a2856',
borderWidth: 5,
},
interest: {
color: '#fff',
backgroundColor: '#1a2856',
paddingLeft: 5,
paddingRight: 5,
},
days: {
borderColor: '#1a2856',
borderWidth: 5,
paddingTop: 10,
paddingLeft: 16,
paddingRight: 6,
fontSize: 18,
},
});
Issues
Both loanAmountArray and durationArray are defined in the function body, so they are actually redeclared each render cycle, so any mutations you thought you did in the previous render cycle are wiped out. When this happens the style attribute is never able to match anything different each render.
Solution
Since it seems you don't really update the elements of the array you can move the arrays out of the component. They can remain const and won't be redeclared each render.
Don't bother trying to update the selected property of each element in the array, you can easily derive the selected state from the selectedDurationId and selectedAmountId state values and the current item.id when rendering.
Use the extraData prop to indicate the list should rerender.
Code:
const loanAmountArray = [
{
id: 1,
amount: "5,000",
selected: "yes"
},
{
id: 2,
amount: "10,000",
selected: "no"
},
{
id: 3,
amount: "20,000",
selected: "no"
}
];
const durationArray = [
{
id: 1,
days: "30 days",
rate: "3.3% Interest",
selected: "yes"
},
{
id: 2,
days: "60 days",
rate: "5% Interest",
selected: "no"
},
{
id: 3,
days: "90 days",
rate: "7% Interest",
selected: "no"
}
];
export default function Loans({}) {
const [selectedDurationId, setSelectedDurationId] = useState(1);
const [selectedAmountId, setSelectedAmountId] = useState(1);
const changeSelectedDuration = function (id) {
setSelectedDurationId(id);
};
const changeSelectedAmount = function (id) {
setSelectedAmountId(id);
};
return (
<View style={{ flex: 1 }}>
<ScrollView>
<View style={styles.contain}>
<Text>Chose Loan Amount</Text>
<FlatList
numColumns={6}
data={loanAmountArray}
extraData={selectedAmountId} // <-- prop used to rerender
keyExtractor={(item, index) => {
return index.toString();
}}
renderItem={({ item }) => {
return (
<View>
<TouchableOpacity
onPress={() => {
changeSelectedAmount(item.id);
}}
>
<Text
style={
item.id === selectedAmountId // <-- match id property
? styles.textBoxSelected
: styles.textBox
}
>
{item.amount}
</Text>
</TouchableOpacity>
</View>
);
}}
/>
<Text>Chose Payment Duration</Text>
<FlatList
numColumns={3}
data={durationArray}
extraData={selectedDurationId} // <-- prop used to rerender
keyExtractor={(item, index) => {
return index.toString();
}}
renderItem={({ item }) => {
return (
<View>
<TouchableOpacity
style={
item.id === selectedDurationId // <-- match id property
? styles.durationViewPressed
: styles.durationView
}
onPress={() => {
changeSelectedDuration(item.id);
}}
>
<View>
<Text style={styles.interest}>{item.rate}</Text>
</View>
<View>
<Text style={styles.days}>{item.days}</Text>
</View>
</TouchableOpacity>
</View>
);
}}
/>
</View>
</ScrollView>
</View>
);
}
Expo Snack Demo

react-native , customer flatlist picker, returning previous values on selection

Below is the code component for the customer picker
import React, { useEffect, useState } from "react";
import { connect } from 'react-redux';
import {
TouchableOpacity,
FlatList,
SafeAreaView,
StatusBar,
StyleSheet,
Text,
View,
Button,
Alert,
} from "react-native";
import { screenHeight, screenWidth } from "../constants";
const DATA = [
{
id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
title: "Client Not Found"
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d72",
title: "Client refused"
},
];
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.item, style]}>
<Text style={styles.title}>{item.title}</Text>
</TouchableOpacity>
);
const StatusOptions = (props) => {
const [selectedId, setSelectedId] = useState(null);
const renderSeparator = () => (
<View
style={{
backgroundColor: "grey",
height: 0.8
}}
/>
);
const ListHeader = () => {
//View to set in Header
return (
<View style={{ height: 20 }}></View>
);
};
const renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "#6cd9ff" : "white";
return (
<Item
item={item}
onPress={() => {
setSelectedId(item.id);
console.log("SELECTED ID _ STATUSOPTIONS component : ", selectedId);
const val = DATA.filter(status => status.id == selectedId).map(filteredStatus => filteredStatus.title);
console.log("VALLLLLLLLLLLLLLLLLLLLLLLUUUUUUEEEEEEEEEEEEEEEEEEEE:::: ", val);
props.navigation.navigate('AnotherScreen');
}}
style={{ backgroundColor }}
/>
);
};
return (
<View style={{ bottom: 0, flex: 1, position: 'absolute', width: screenWidth, }}>
<View style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
ItemSeparatorComponent={renderSeparator}
ListHeaderComponent={ListHeader}
style={{
backgroundColor: "white",
width: "100%",
borderTopRightRadius: 20,
borderTopLeftRadius: 20,
zIndex: 1,
}}
/>
</View>
<View style={{ backgroundColor: "grey", height: 0.4 }} />
<View style={styles.closeButtonContainer}>
<TouchableOpacity style={styles.closeButton}
onPress={() => {
props.setStatusOptionsVisible(false)
}}>
<Text style={styles.title}>Close</Text>
</TouchableOpacity>
</View>
</View>
);
};
function mapStateToProps(state) {
return {
StatusOptionsVisible: state.volunteerItems.statusOptionsVisible,
currentTaskItemId: state.taskItems.taskItemId,
};
}
function mapDispatchToProps(dispatch) {
return {
setStatusOptionsVisible: (visible) => dispatch({ type: 'SET_STATUS_VISIBLE', statusVisibility: visible }),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(StatusOptions);
const styles = StyleSheet.create({
closeButton: {
backgroundColor: 'lightgrey',
borderRadius: 10,
height: 50,
justifyContent: 'center',
width: '90%',
},
closeButtonContainer: {
alignItems: 'center',
height: 90,
justifyContent: 'center',
backgroundColor: 'white',
},
textStyle: {
textAlign: "center",
},
container: {
borderRadius: 30,
flex: 4,
width: screenWidth,
},
item: {
padding: 20
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: "black",
textAlign: "center"
}
});
the console Log : ("SELECTED ID _ STATUSOPTIONS component : ", selectedId)
in render Item function returns null for first picker item selection and the returns the previous value for the next picker item selection , can anyone please help with fixing it ?
Try to use this
useEffect(() => {
console.log("SELECTED ID _ STATUSOPTIONS component : ", selectedId);
if(selectedId != null) {
const val = DATA.filter(status => status.id == selectedId).map(filteredStatus => filteredStatus.title);
console.log("VALLUUEEEEEEEEEEEEEEEEEEEE:::: ", val);
}
}, [selectedId])

underline the clicked text element

I have a list of text elements that I want to underline when clicked. If I add the text decoration to the tabText then obviously it is applied to all items. How can I make sure that the when I click on another tab, the underline from the previous tab gets removed?
Is there any way I can add or remove items from the style element upon clicking on an item?
//onPress={() => {}}>
const tabs = [
{
id: 1,
text: 'Alle List',
},
{
id: 2,
text: 'Second',
},
];
export const Tab: React.FunctionComponent = () => {
return (
<View style={styles.tabView}>
{tabs.map((item: any) => (
<View>
<Text style={styles.tabText}>{item.text}</Text>
</View>
))}
</View>
);
};
const styles = StyleSheet.create({
tabView: {
paddingTop: moderateScale(15),
paddingLeft: moderateScale(20),
paddingRight: moderateScale(20),
flexDirection: 'row',
justifyContent: 'space-between',
},
tabText: {
color: 'white',
paddingBottom: moderateScale(10),
//textDecorationLine: 'underline'
},
});
Codesandbox (with tabText items as an array too):
https://snack.expo.io/#nhammad/shallow-watermelon
You can use useState to save the selected index and then apply another style based on the selected index. Here is a quick modification to your script and the snack link is at the end.
import * as React from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
const tabs = [
{
id: 1,
text: 'All Friends',
},
{
id: 2,
text: 'Second',
},
{
id: 3,
text: 'Third',
},
];
export default function App() {
const [selected, setSelected] = React.useState(null)
return (
<View style={styles.container}>
<View style={styles.tabView}>
{tabs.map((item: any, index) => (
<TouchableOpacity onPress={()=>setSelected(index)}>
<View>
<Text style={[styles.tabText,selected===index?styles.selected:null]}>{item.text}</Text>
</View>
</TouchableOpacity>
))}
</View>
</View>
);
}
const styles = StyleSheet.create({
tabView: {
paddingTop: 15,
paddingLeft: 20,
paddingRight: 20,
flexDirection: 'row',
justifyContent: 'space-between',
},
tabText: {
color: 'black',
paddingBottom: 10,
},
selected: {
textDecorationLine: 'underline'
},
});
https://snack.expo.io/#saachitech/da6280
You can add different styles for your tab depending on active route
<Text style={[(this.props.activeRouteName == route.routeName) ? styles.tabActiveText: styles.tabText]}>{item.text}</Text>
And then in the styles
tabActiveText: {
color: 'white',
paddingBottom: moderateScale(10),
textDecorationLine: 'underline'
}

Categories