I'm trying to display the list of contacts and in IOS it works fine but in ANDROID I get this error:
undefined is not an object (evaluating 'item.phoneNumbers[0]').
So far I have read that the reason this error comes up is because I should check first that a phone number exists before displaying it but that is something that I have already done in my code and I still get this error. If there is something I'm getting it wrong please let me know. Below I'll attach my code.
function ContactList({ selectedContacts, setSelectedContacts, contacts, selectedGuardian, setSelectedGuardian }) {
const [checked, setChecked] = useState(false);
const [filter, setFilter] = useState([]);
const [search, setSearch] = useState('');
const [checkedBox, setCheckedBox] = useState(false);
useEffect(() => {
setFilter(contacts);
}, [contacts]);
const searchFilter = (text) => {
if (text) {
const newData = contacts.filter((item) => {
const itemData = item.name ? item.name.toUpperCase() : ''.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setFilter(newData);
setSearch(text);
} else {
setFilter(contacts);
setSearch(text);
}
};
// console.log('contacts: ',contacts);
const onChangeValue = (item) => {
const fullName = item.firstName;
const phoneNumbers = item.phoneNumbers[0].digits;
const phoneNumberAndroid = item.phoneNumbers[0].number;
{Platform == 'ios' ? setSelectedContacts({ ...selectedContacts, [phoneNumbers]: !selectedContacts[phoneNumber] }) : setSelectedContacts({ ...selectedContacts, [phoneNumberAndroid]: !selectedContacts[phoneNumberAndroid] })};
{Platform == 'ios' ? setSelectedGuardian([...selectedGuardian, { name: fullName, phone: phoneNumbers }]) : setSelectedGuardian([...selectedGuardian, { name: fullName, phone: phoneNumberAndroid }]) };
};
const renderItem = ({ item, index }) => {
const phoneNumberAndroid = item.phoneNumbers[0].number;
const phoneNumbers = item.phoneNumbers[0].digits;
return (
<SafeAreaView>
<ScrollView>
<TouchableOpacity
onPress={() => {
onChangeValue(item);
}}
style={{ flex: 1, flexDirection: 'row', borderTopWidth: 0.5, borderTopColor: 'grey' }}
>
<View style={{ flex: 1 }}>
<Text onPress={() => setChecked(true)} style={{ fontSize: 20, marginHorizontal: 10 }}>
{item.name + ' '}
</Text>
<Text style={{ fontSize: 17, marginHorizontal: 10, marginTop: 5, color: 'grey' }}>
{item.phoneNumbers && item.phoneNumbers[0] && item.phoneNumbers[0].number}
</Text>
</View>
<CheckBox
style={{ width: 15, height: 15, paddingTop: 8 }}
right={true}
checked={ Platform == 'ios' ? !!selectedContacts[phoneNumbers] : !!selectedContacts[phoneNumberAndroid]}
onPress={() => {
onChangeValue(item, index);
}}
/>
</TouchableOpacity>
</ScrollView>
</SafeAreaView>
);
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<View
style={{
height: 40,
justifyContent: 'center',
backgroundColor: '#eeeeee',
width: '90%',
marginHorizontal: 20,
marginTop: 15,
borderRadius: 10,
}}
>
<Feather name="search" size={20} color="grey" style={{ position: 'absolute', left: 32 }} />
<TextInput
placeholder="Search"
placeholderTextColor="#949494"
style={{
left: 20,
paddingHorizontal: 35,
fontSize: 20,
}}
value={search}
onChangeText={(text) => {
searchFilter(text);
setSearch(text);
}}
/>
</View>
<FlatList
style={{ marginTop: 15 }}
data={filter}
keyExtractor={(item) => `key-${item.id.toString()}`}
renderItem={renderItem}
refreshing={true}
initialNumToRender={10}
ListEmptyComponent={<Text message="No contacts found." />}
/>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
export default ContactList;
I first focused at this part of your code:
{Platform == 'ios' ? setSelectedContacts({ ...selectedContacts, [phoneNumbers]: !selectedContacts[phoneNumber] }) : setSelectedContacts({ ...selectedContacts, [phoneNumberAndroid]: !selectedContacts[phoneNumberAndroid] })};
{Platform == 'ios' ? setSelectedGuardian([...selectedGuardian, { name: fullName, phone: phoneNumbers }]) : setSelectedGuardian([...selectedGuardian, { name: fullName, phone: phoneNumberAndroid }]) };
From what I've noticed here, the difference if the Platform is iOS or Android is that phoneNumbers is used in iOS while phoneNumberAndroid is used in Android!
Then, I saw the 2 lines of code above:
const phoneNumbers = item.phoneNumbers[0].digits;
const phoneNumberAndroid = item.phoneNumbers[0].number;
If I'm not mistaken, Android here uses item.phoneNumbers[0].number.
So, the only reason I could think of the error showing is that item.phoneNumbers[0].number is invalid!
Though I'm not sure of this, I couldn't think of any other reason. Sorry if this is incorrect!
Related
I am encountering the "java.lang.Double cannot be cast to abie47_0_0.com.facebook.react.bridge.ReadableMap" error when opening the app with my android while using react-native-gifted-charts.
This error showing after using react-native-gifted charts
this is the code of the screen, can anyone help or advise what could be the problem and ways to resolve it
Error I got
Below is the code
import styles from "./styles";
import CoinDetailedHeader from "./components/CoinDetailedHeader/index";
import { AntDesign } from "#expo/vector-icons";
import { ChartDot, ChartPath, ChartPathProvider, ChartYLabel } from "#rainbow-me/animated-charts";
const CoinDetailedScreen = () => {
const {
image: { small },
name,
symbol,
prices,
market_data: { market_cap_rank, current_price, price_change_percentage_24h, },
} = Coin;
const [coinValue, setCoinValue] = useState("1");
const [usdValue, setUsdCoinValue] = useState(current_price.usd.toString());
const percentageColor = price_change_percentage_24h < 0 ? '#ea3943' : '#16c784';
const chartColor = current_price.usd > prices[0][1] ? '#16c784' : '#ea3943';
const screenWidth = Dimensions.get('window').width;
const changeCoinValue = (value) => {
setCoinValue(value);
const floatValue = parseFloat(coinValue) || 0;
setUsdCoinValue((floatValue * current_price.usd).toString());
};
const changeUsdValue = (value) => {
setUsdCoinValue(value);
const floatValue = parseFloat(value.replace(',', '.')) || 0;
setCoinValue((floatValue / current_price.usd).toString());
}
const formatCurrency = (value) => {
"worklet";
if (value === "") {
return `$${current_price.usd.toFixed(2)}`
}
return `$${parseFloat(value).toFixed(2)}`
}
return (
<View style={{ paddingHorizontal: 10 }}>
<ChartPathProvider data={{
points: prices.map(([x, y]) => ({ x, y })),
smoothingStrategy: 'bezier',
}}>
<CoinDetailedHeader
image={small}
symbol={symbol}
marketCapRank={market_cap_rank}
/>
<View style={styles.priceContainer}>
<View>
<Text style={styles.name}>{name}</Text>
<ChartYLabel
format={formatCurrency}
style={styles.currentPrice}
/>
</View>
<View style={{ backgroundColor: percentageColor, paddingVertical: 8, paddingHorizontal: 3, borderRadius: 5, flexDirection: 'row' }}>
<AntDesign name={price_change_percentage_24h < 0 ? 'caretdown' : 'caretup'}
size={12} color={'white'} style={{ alignSelf: 'center', marginRight: 5 }} />
<Text style={styles.priceChange}>{price_change_percentage_24h.toFixed(2)}%</Text>
</View>
</View>
<ChartPath height={screenWidth / 2} strokeWidth={2} stroke={chartColor} width={screenWidth} />
<ChartDot style={{ backgroundColor: chartColor }} />
<View style={{ flexDirection: 'row' }}>
<View style={{ flexDirection: 'row', flex: 1 }}>
<Text style={{ color: 'white', alignSelf: 'center' }}>{symbol.toUpperCase()}</Text>
<TextInput style={styles.input} value={coinValue} keyboardType='numeric' onChangeText={changeCoinValue} />
</View>
<View style={{ flexDirection: 'row', flex: 1 }}>
<Text style={{ color: 'white', alignSelf: 'center' }}>USD</Text>
<TextInput style={styles.input} value={usdValue} keyboardType='numeric' onChangeText={changeUsdValue} />
</View>
</View>
</ChartPathProvider>
</View>
);
};
export default CoinDetailedScreen;
Try updating following packages to mentioned versions
"react-native-linear-gradient": "2.6.2",
"react-native-svg": "12.1.0",
Ref : java.lang.Double cannot be cast to com.facebook.react.bridge.ReadableMap
I have an issue with modal not being stable when text input receives entry. I have struggled with this and read most post on this site but when I try it, no results. One of them is Keyboard closes after every keypress in TextInput react native. I have taken most of the irrelevant code out so you have an idea of what I want to achieve. It's quiet lengthy but I tried.
import React, { useEffect, useState } from "react";
/*
loads of imports
**/
const ReplyFeedback = (props) => {
const [dataset, setdataset] = useState([]);
const [replyset, setreplyset] = useState([]);
const [visible, setvisible] = useState(null);
const [messagereply, setmessagereply] = useState("");
const [msgid, setmsgid] = useState(null);
const dispatch = useDispatch();
/**
* useEffects and functions
*/
const handleSaveReply = (id) => {
/**
* Saving the reply from text input for firebase
*/
};
const handleCaseupdate = async () => {
/**
* Some firebase events
*/
};
function processreply(id) {
setmsgid(id);
setvisible(true);
}
const renderfeedback = (id, message, date, index) => (
/**
* reder some jsx
*/
);
const renderreply = (item, index) => (
/**
* render some jsx
*/
);
const ModalPopup = ({ visible, children }) => {
const [showmodal, setshowmodal] = useState(visible);
const togglemodal = () => {
if (visible) {
setshowmodal(true);
} else {
setshowmodal(false);
}
};
return (
<Modal transparent visible={showmodal}>
<View
style={{
flex: 1,
backgroundColor: "rgba(0,0,0,0.5)",
justifyContent: "center",
alignItems: "center",
}}
>
<View style={styles.modalContainer}>{children}</View>
</View>
</Modal>
);
};
return (
<React.Fragment>
<SafeAreaView style={styles.container}>
<KeyboardAvoidingView behavior="position">
<ModalPopup visible={visible}>
<View
style={{
width: "90%",
height: "72%",
backgroundColor: "rgb(255,255,255)",
}}
>
<View
style={{
flexDirection: "row",
justifyContent: "center",
}}
>
<TextInput
placeholder="Enter Reply"
multiline
autoCorrect
scrollEnabled
numberOfLines={8}
autoCapitalize="sentences"
placeholderTextColor="rgb(0,191,255)"
onChangeText={(text) => setmessagereply(text)}
value={messagereply}
blurOnSubmit={false}
style={{
width: "100%",
height: 250,
fontSize: 20,
padding: 10,
textAlignVertical: "top",
backgroundColor: "rgb(245,245,245)",
}}
/>
</View>
<View
style={{
marginTop: 15,
flexDirection: "row",
justifyContent: "space-around",
}}
>
<TouchableOpacity
style={{
width: 65,
height: 65,
borderRadius: 30,
alignItems: "center",
justifyContent: "center",
backgroundColor: "rgb(109, 123, 175)",
}}
onPress={() => setvisible(false)}
>
<MaterialIcons
name="cancel"
size={50}
color="rgb(225,225,225)"
/>
</TouchableOpacity>
<TouchableOpacity
onPress={() => handleSaveReply(msgid)}
style={{
width: 65,
height: 65,
borderRadius: 30,
alignItems: "center",
justifyContent: "center",
backgroundColor: "rgb(109, 123, 175)",
}}
>
<Ionicons name="send" size={40} color="rgb(255,255,255)" />
</TouchableOpacity>
</View>
</View>
</ModalPopup>
</KeyboardAvoidingView>
<View style={styles.feedbackreply}>
<View style={styles.replydetails}>
{/**
* some jsx
*/}
</View>
{/**
* somejsx
*/}
</View>
<View
style={{
alignSelf: "flex-end",
marginHorizontal: 5,
marginTop: 5,
}}
>
{/**
* some jsx
*/}
</View>
<ScrollView
automaticallyAdjustContentInsets={false}
keyboardShouldPersistTaps="handled"
>
{dataset &&
dataset
.sort(
(a, b) =>
new moment(new Date(a.date)).format("YYYYMMDD HHmmss") -
new moment(new Date(b.date)).format("YYYYMMDD HHmmss")
)
.map(({ id, message, date }, index) =>
renderfeedback(id, message, date, index)
)}
<ScrollView keyboardShouldPersistTaps="handled">
{replyset &&
replyset
.sort(
(a, b) =>
new moment(new Date(a.date)).format("YYYYMMDD HHmmss") -
new moment(new Date(b.date)).format("YYYYMMDD HHmmss")
)
.filter((item) => item.id !== id)
.map((item, index) => renderreply(item, index))}
</ScrollView>
</ScrollView>
</SafeAreaView>
</React.Fragment>
);
};
export { ReplyFeedback };
const window = Dimensions.get("window");
const styles = StyleSheet.create({
/**
* Tones of styling
*/
});
So you could see from the container I rendered some other views including a scrollview that all appears under the modal when it is shown. Please the issue here is that the modal flickers along with the keyboard making it impossible to add text
This is the image
I am trying to work through a problem with my array state in React Native and correctly updating the array of objects when I click a button on one of the components.
Here is a video of the problem occurring, which is probably a better way to explain the issue: https://cdn.discordapp.com/attachments/935693286566416417/953045147397001276/RPReplay_Final1647294055.MP4
Here's the relevant code that might offer some insight:
MealPlanner.Js
const MealPlannerScreen = props => {
const minDate = new Date();
const [mealPlannerData, setMealPlannerData] = useState([]);
const [loading, setLoading] = useState(true)
const [selectedDate, setSelectedDate] = useState("03/13/2022")
const [breakfast, setBreakfast] = useState([])
const getMealPlannerData = async(date = '03/13/2022') => {
const isDataAvailable = await AsyncStorage.getItem("meal_planner")
if (isDataAvailable) {
const parsedData = JSON.parse(isDataAvailable)
let breakfastData = parsedData.filter(function (e) {
return ((e.date = date) && (e.meal == 'Breakfast'));
});
let lunchData = parsedData.filter(function (e) {
return ((e.date = date) && (e.meal == 'Lunch'));
});
let dinnerData = parsedData.filter(function (e) {
return ((e.date = date) && (e.meal == 'Dinner'));
});
let snacksData = parsedData.filter(function (e) {
return ((e.date = date) && (e.meal == 'Snacks'));
});
setMealPlannerData(JSON.parse(isDataAvailable))
setBreakfast(breakfastData)
setLunch(breakfastData)
setDinner(dinnerData)
setSnacks(snacksData)
} else {
setMealPlannerData([])
}
}
const childToParent = async(unique_id, meal, date) => {
const mealFiltered = mealPlannerData.filter(function (e) {
return e.unique_id != unique_id;
});
console.log(unique_id)
console.log(date)
if (meal === 'Breakfast') {
let breakfastData = mealFiltered.filter(function (e) {
return ((e.date = date) && (e.meal == 'Breakfast'));
});
console.log(breakfastData);
setBreakfast(breakfastData)
}
return (
<Container style={styles.background_general}>
<View>
{breakfast.map((p, i) => {
console.log(breakfast)
return (<>
<FoodItem meal={"Breakfast"} date={p.date} food={p.recipe_name} calories={p.calories} servings={p.servings} unique_id={p.unique_id} mealData={mealPlannerData} childToParent={childToParent}/>
<View style={{
borderBottomColor: 'rgba(0,0,0,0.1)',
borderBottomWidth: 1,
}}>
</View>
</>
)
})}
</View>
</Container>
}
}
And the FoodItem.js
const FoodItem = props => {
removeMeal = (unique_id, meal) => {
props.childToParent(unique_id, meal, props.date)
}
const renderRightActions = useCallback((progress, dragX) => {
const trans = dragX.interpolate({
inputRange: [-300, -160, 0],
outputRange: [0, 0, 160],
});
return (
<Animated.View
style={{
transform: [{ translateX: trans }],
}}
>
<LinearGradient
colors={["#6e269b", "#35184e"]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={{
flexDirection: "row",
width: 160,
height: "100%",
borderRadius: 20
}}
>
<RectButton style={{
justifyContent: "center",
alignItems: "center",
paddingHorizontal: 16,
flex: 1
}} onPress={() => {removeMeal(props.unique_id, props.meal) }}>
<View style={{ flexDirection: 'row' }}>
<Icon type="FontAwesome" name="trash" style={{ fontSize: 18, color: 'white', paddingRight: 10, marginTop: 2 }} />
<Text style={{ color: '#fff', fontWeight: 'bold', fontSize: 18 }}>
Remove
</Text>
</View>
</RectButton>
</LinearGradient>
</Animated.View>);
}, []);
return (
<View style={{
padding: 20
}}>
<Swipeable renderRightActions={renderRightActions}>
<View padding-6 row>
<View style={{ alignItems: "center" }}>
</View>
<View >
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>
{props.food}
</Text>
<View row marginT-8>
<View row>
<Text R14 color6D marginL-4>
{props.calories} cal
</Text>
</View>
<View row marginL-24>
<Text R14 color6D marginL-4>
{props.servings} servings
</Text>
</View>
</View>
</View>
</View>
</Swipeable>
</View>
)
}
Any help would be greatly appreciated!
Srry if the title makes no sense. Don't know a better title.
How can I save route.params items that I pass to my second screen using AsyncStorage?
In my first screen i have a bunch of data in a FlatList that can be opened with a Modal. Inside that Modal I have a TouchableOpacity that can send the data thats inside the Modal to my second screen. The data that has been passed to the second screen is passed to a FlatList. The data in the FlatList should be saved to AsyncStorage. Tried alot of things getting this to work, but only getting warning message
undefined. Code below is the most recent progress.
Using React Navigation V5.
FIRST SCREEN
const [masterDataSource, setMasterDataSource] = useState(DataBase);
const [details, setDetails] = useState('');
<TouchableOpacity
onPress={() => {
const updated = [...masterDataSource];
updated.find(
(item) => item.id === details.id,
).selected = true;
setMasterDataSource(updated);
navigation.navigate('cart', {
screen: 'cart',
params: {
items: updated.filter((item) => item.selected),
},
});
setModalVisible(false);
}}>
<Text>Add to cart</Text>
</TouchableOpacity>
SECOND SCREEN
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, TouchableOpacity } from 'react-native';
import { useTheme } from '../Data/ThemeContext';
import AsyncStorage from '#react-native-async-storage/async-storage';
import Ionicons from 'react-native-vector-icons/Ionicons';
export default function ShoppingList({ route, navigation }) {
const [shoppingList, setShoppingList] = useState([]);
const { colors } = useTheme();
const todo = () => {
alert('Todo');
};
useEffect(() => {
restoreShoppingListAsync();
}, []);
const shoppingListAsync = () => {
const shoppingList = route.params && route.params.items;
setShoppingList(list);
storeShoppingList(list);
};
const asyncStorageKey = '#ShoppingList';
const storeShoppingListAsync = (list) => {
const stringifiedList = JSON.stringify(list);
AsyncStorage.setItem(asyncStorageKey, stringifiedList).catch((err) => {
console.warn(err);
});
};
const restoreShoppingListAsync = () => {
AsyncStorage.getItem(asyncStorageKey)
.then((stringifiedList) => {
console.log(stringifiedList);
const parsedShoppingList = JSON.parse(stringifiedList);
if (!parsedShoppingList || typeof parsedShoppingList !== 'object')
return;
setShoppingList(parsedShoppingList);
})
.then((err) => {
console.warn(err);
});
};
const RenderItem = ({ item }) => {
return (
<View>
<TouchableOpacity
style={{
marginLeft: 20,
marginRight: 20,
elevation: 3,
backgroundColor: colors.card,
borderRadius: 10,
}}>
<View style={{ margin: 10 }}>
<Text style={{ color: colors.text, fontWeight: '700' }}>
{item.name}
</Text>
<Text style={{ color: colors.text }}>{item.gluten}</Text>
<Text style={{ color: colors.text }}>{item.id}</Text>
</View>
</TouchableOpacity>
</View>
);
};
const emptyComponent = () => {
return (
<View style={{ alignItems: 'center' }}>
<Text style={{ color: colors.text }}>Listan är tom</Text>
</View>
);
};
const itemSeparatorComponent = () => {
return (
<View
style={{
margin: 3,
}}></View>
);
};
return (
<View
style={{
flex: 1,
}}>
<View
style={{
padding: 30,
backgroundColor: colors.Textinput,
elevation: 12,
}}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<Ionicons name="arrow-back-outline" size={25} color="#fff" />
</TouchableOpacity>
<Text style={{ color: '#fff', fontSize: 20 }}>Inköpslista</Text>
<TouchableOpacity>
<Ionicons
name="trash-outline"
size={25}
color="#fff"
onPress={() => todo()}
/>
</TouchableOpacity>
</View>
</View>
<View style={{ flex: 1, marginTop: 30 }}>
<FlatList
data={shoppingList}
renderItem={RenderItem}
ListEmptyComponent={emptyComponent}
ItemSeparatorComponent={itemSeparatorComponent}
initialNumToRender={4}
maxToRenderPerBatch={5}
windowSize={10}
removeClippedSubviews={true}
updateCellsBatchingPeriod={100}
showsVerticalScrollIndicator={true}
contentContainerStyle={{ paddingBottom: 20 }}
/>
</View>
</View>
);
}
As you are using async storage to maintain the cart.
I would suggest an approach as below
Update the asyn storage when new items are added to or removed from the cart
Retrieve the items from the cart screen and show the items there
Before you navigate store the items like below
AsyncStorage.setItem(
'Items',
JSON.stringify(updated.filter((item) => item.selected))
).then(() => {
navigation.navigate('Cart', {
items: updated.filter((item) => item.selected),
});
});
The cart screen would be something like below
function Cart({ navigation, route }) {
const [data,setData]=useState([]);
React.useEffect(() => {
async function fetchMyAPI() {
const value = await AsyncStorage.getItem('Items');
setData(JSON.parse(value));
}
fetchMyAPI();
}, []);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Go back" onPress={() => navigation.goBack()} />
<FlatList
data={data}
renderItem={RenderItem}
/>
</View>
);
}
Working Example
https://snack.expo.io/#guruparan/cartexample
I am generating JSX code based on an array values that I get from a backend API. As shown in the image below I am generating those boxes based on the length of the array. What I want is when I click on any of those boxes the background colour changes.
I want these boxes to behave like a radio button, so only one box has a different background colour at a time. The array name is "hasMultipleWeights".
I only included the relevant parts of the code ...
const ProductDetailsScreen = (props) => {
const productId = props.navigation.getParam("productId");
const selectedProduct = useSelector((state) =>
state.products.products.find((prod) => prod.id === productId)
);
const productsMultipleWeights = useSelector(
(state) => state.products.productsMultipleWeights
);
var hasMultipleWeights = productsMultipleWeights.find(
(prod) => Object.keys(prod)[0] == selectedProduct.id
);
if (hasMultipleWeights) {
hasMultipleWeights = hasMultipleWeights[Object.keys(hasMultipleWeights)[0]];
}
return (
<ScrollView style={{}}>
<View style={styles.screen}>
{hasMultipleWeights && (
<View style={{ alignItems: "center" }}>
<ScrollView
horizontal
contentContainerStyle={{ padding: 2 }}
showsHorizontalScrollIndicator={false}
>
{hasMultipleWeights.map((item) => (
<TouchableOpacity
key={item.id}
onPress={() => {}}
style={{
...styles.productOptions,
backgroundColor: 'white',
}}
>
<Text style={styles.productWeightVolumUnit}>
<Text style={styles.productWeightVolumeValue}>
{NS(item.weight, "Arabic")}
</Text>
{" "}
{selectedProduct.weightVolumeUnit}
</Text>
<MaterialCommunityIcons
name={
selectedProduct.weightVolumeUnit === "كغ"
? "weight-kilogram"
: selectedProduct.weightVolumeUnit === "مل"
? "water"
: "weight-gram"
}
size={26}
color="grey"
style={styles.weightIcon}
/>
</TouchableOpacity>
))}
</ScrollView>
</View>
)}
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
productOptions: {
shadowColor: "black",
shadowOpacity: 0.26,
elevation: 5,
borderRadius: 10,
backgroundColor: "white",
width: 85,
height: 65,
marginHorizontal: 6,
alignItems: "center",
justifyContent: "center",
shadowRadius: 2,
shadowOffset: { width: 0, height: 1 },
},
});
For changing the color dynamically you have to use state. So, create a new state to check the button that is "checked", you change it in your onPress method and then make the validation.
const ProductDetailsScreen = (props) => {
const [checkedButton, setCheckedButton] = React.useState('')
const productId = props.navigation.getParam("productId");
const selectedProduct = useSelector((state) =>
state.products.products.find((prod) => prod.id === productId)
);
const productsMultipleWeights = useSelector(
(state) => state.products.productsMultipleWeights
);
var hasMultipleWeights = productsMultipleWeights.find(
(prod) => Object.keys(prod)[0] == selectedProduct.id
);
if (hasMultipleWeights) {
hasMultipleWeights = hasMultipleWeights[Object.keys(hasMultipleWeights)[0]];
}
return (
<ScrollView style={{}}>
<View style={styles.screen}>
{hasMultipleWeights && (
<View style={{ alignItems: "center" }}>
<ScrollView
horizontal
contentContainerStyle={{ padding: 2 }}
showsHorizontalScrollIndicator={false}
>
{hasMultipleWeights.map((item) => (
<TouchableOpacity
key={item.id}
onPress={() => setCheckedButton(item.id)}
style={{
...styles.productOptions,
backgroundColor: checkedButton === item.id ? "grey" : "white",
}}
>
<Text style={styles.productWeightVolumUnit}>
<Text style={styles.productWeightVolumeValue}>
{NS(item.weight, "Arabic")}
</Text>
{" "}
{selectedProduct.weightVolumeUnit}
</Text>
<MaterialCommunityIcons
name={
selectedProduct.weightVolumeUnit === "كغ"
? "weight-kilogram"
: selectedProduct.weightVolumeUnit === "مل"
? "water"
: "weight-gram"
}
size={26}
color="grey"
style={styles.weightIcon}
/>
</TouchableOpacity>
))}
</ScrollView>
</View>
)}
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
productOptions: {
shadowColor: "black",
shadowOpacity: 0.26,
elevation: 5,
borderRadius: 10,
backgroundColor: "white",
width: 85,
height: 65,
marginHorizontal: 6,
alignItems: "center",
justifyContent: "center",
shadowRadius: 2,
shadowOffset: { width: 0, height: 1 },
},
});
Seems like you need to create checkedId state
const [ checkedId, setCheckedId ] = useState('')
useEffect(() =>
// set first box to red at first render
hasMultipleWeights && setCheckedId(hasMultipleWeights[0].id) ,[ hasMultipleWeights ])
...
<TouchableOpacity
key={item.id}
onPress={() =>setCheckedId(item.id)}
style={{
...styles.productOptions,
backgroundColor: item.id == checkedId ? 'red' : 'white',
}}
>