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!
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'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!
I am working on a register page on react native where I want to make an onboarding register page so I have my TextInputs in a separate function to switch between the text input when you go to the next onboard screen. But when I press on an input and want to start typing I can get one press of on the keyboard before it goes down again.
I have tried moving my useEffect inside that function then the issue is solved but the other functions cannot get the values then.
//Content
export default function Register({ navigation }) {
const [name, onNameChange] = useState("");
//get current slide index point
const [currentSlideIndex, setCurrentSlideIndex] = useState(0);
const ref = React.useRef(null);
//update slides upon index
const updateCurrentSlideIndex = (e) => {
const contentOffsetX = e.nativeEvent.contentOffset.x;
const currentIndex = Math.round(contentOffsetX / width);
setCurrentSlideIndex(currentIndex);
};
//button function to go to next slide
const goNextSlide = () => {
const nextSlideIndex = currentSlideIndex + 1;
if (nextSlideIndex != slides.length) {
const offset = nextSlideIndex * width;
ref?.current?.scrollToOffset({ offset });
setCurrentSlideIndex(nextSlideIndex);
}
};
const RegInputs = () => {
if (currentSlideIndex == 0) {
return (
<KeyboardAvoidingView style={{ flex: 1 }} behavior="position">
<BlurView intensity={20} tint="light">
<Text style={styles.label}>Name</Text>
<TextInput
value={name}
onEndEditing={(text) => onNameChange(text)}
style={styles.input}
errorStyle={{ color: "red" }}
errorMessage="Incorrect Username"
/>
</BlurView>
</KeyboardAvoidingView>
);
} else if (currentSlideIndex == 1) {
return <View></View>;
} else if (currentSlideIndex == 2) {
return <View></View>;
}
};
const handleRegisterPress = () => {
Alert.alert("NAME: " + name);
};
const Slide = ({ item }) => {
return (
<View style={{ alignItems: "center" }}>
<View style={{ width }}>
<Image
source={item.image}
style={{
width: imagewidth,
height: imageHeight,
resizeMode: "contain",
alignSelf: "center",
}}
/>
</View>
<Text style={styles.heading}>{item.title}</Text>
<RegInputs />
</View>
);
};
const Footer = () => {
return (
<View style={{ height: height * 0.15, width, paddingHorizontal: 20 }}>
<View
style={{
flexDirection: "row",
justifyContent: "center",
marginTop: 20,
}}
>
{slides.map((_, index) => (
<View
key={index}
style={[
styles.indicator,
currentSlideIndex == index && {
backgroundColor: "white",
borderColor: "#68BF7B",
borderWidth: 2,
width: 16,
opacity: 1,
},
]}
/>
))}
</View>
<View style={{ marginBottom: 20 }}>
{currentSlideIndex == slides.length - 1 ? (
<View>
<TouchableOpacity onPress={handleRegisterPress}>
<View style={styles.nextbtn}>
<Text style={styles.nextbtnText}>Finish</Text>
</View>
</TouchableOpacity>
</View>
) : (
<TouchableOpacity onPress={goNextSlide}>
<View style={styles.nextbtn}>
<Text style={styles.nextbtnText}>Next</Text>
</View>
</TouchableOpacity>
)}
<View style={{ flexDirection: "row" }}>
<Text style={styles.bodyreg}>Already have an account?</Text>
<TouchableOpacity onPress={() => navigation.navigate("Login")}>
<Text style={styles.bodylinkreg}>Login</Text>
</TouchableOpacity>
</View>
</View>
</View>
);
};
}
change the prop onEndEditing in TextInput to onChangeText, and reload the entire app
import React, {useState, useEffect, useRef} from 'react';
import {
View,
Text,
StyleSheet,
FlatList,
ScrollView,
Image,
TouchableHighlight,
TextInput,
TouchableOpacity,
Modal,
Dimensions,
SafeAreaView,
} from 'react-native';
import Voice from 'react-native-voice';
import Video from 'react-native-video';
import SimpleLineIcons from 'react-native-vector-icons/SimpleLineIcons';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import {videos} from './data';
// code started creating state on every event which will be used
enter center export default function App() {
const [pitch, setPitch] = useState('');
const [error, setError] = useState('');
const [end, setEnd] = useState('');
const [started, setStarted] = useState('');
const [results, setResults] = useState([]);
const [videoData, setVideos] = useState([...videos]);
const [showModal, setShowModal] = useState(false);
const [paused, setPaused] = useState(true);
const [position, setPosition] = useState({start: null, end: null});
const [muted, setMuted] = useState(true);
//Setting callbacks for the process status
useEffect(() => {
Voice.onSpeechStart = onSpeechStart;
Voice.onSpeechEnd = onSpeechEnd;
Voice.onSpeechError = onSpeechError;
Voice.onSpeechResults = onSpeechResults;
return () => {
//destroy the process after switching the screen
Voice.destroy().then(Voice.removeAllListeners);
};
}, []);
const onSpeechStart = (e) => {
//Invoked when .start() is called without error
console.log('onSpeechStart: ', e);
setStarted('√');
};
const onSpeechEnd = (e) => {
//Invoked when SpeechRecognizer stops recognition
console.log('onSpeechEnd: ', e);
setEnd('√');
};
const onSpeechError = (e) => {
//Invoked when an error occurs.
console.log('onSpeechError: ', e);
setError(JSON.stringify(e.error));
};
2. in this part we are matching the word sopken by the user and checking is if the same video is available in our data if it si then the video will come on the top
const onSpeechResults = (e) => {
//Invoked when SpeechRecognizer is finished recognizing
console.log('onSpeechResults: ', e.value[0]);
let vResult = e.value[0];
let small = vResult.toLowerCase();
setResults([small]);
setShowModal(false);
for (let i = 0; i < videoData.length - 1; i++) {
let newArray = [...videos];
if (small == newArray[i].name) {
newArray.splice(0, 0, newArray[i]);
const mainArray = newArray.filter((item, index) => {
if (item.name == small && index != 0) {
} else {
return item;
}
});
setVideos(mainArray);
}
}
};
const startRecognizing = async () => {
console.log('start listenind');
//Starts listening for speech for a specific locale
try {
await Voice.start('en-US');
setPitch('');
setError('');
setStarted('');
setResults([]);
setEnd('');
} catch (e) {
//eslint-disable-next-line
console.error(e);
}
};
const stopRecognizing = async () => {
//Stops listening for speech
try {
await Voice.stop();
} catch (e) {
//eslint-disable-next-line
console.error(e);
}
};
3. making video play n pause is out of axis
onScroll = (event) => {
const scrollPosition = event.nativeEvent.contentOffset.y;
const paused = paused;
const {start, end} = position;
if (scrollPosition < start && scrollPosition > end && paused) {
setPaused(false);
} else if (scrollPosition > start && scrollPosition < end && !paused) {
setPaused(true);
} else if (scrollPosition < end && !paused) {
setPaused(true);
}
};
4. getting the scroll y axis
const threshold = 180;
onVideoLayOut = (event) => {
const {height} = Dimensions.get('window');
const start = -(event.nativeEvent.layout.y - height + threshold);
const end =
event.nativeEvent.layout.y + event.nativeEvent.layout.height - threshold;
// console.log(start, end, 'position');
setPosition((state) => {
return {
...state,
start: start,
end: end,
};
});
console.log(position, 'position1');
};
5.this the render par using faltlist to render data
return (
<View
style={{
flex: 1,
backgroundColor: 'black',
}}>
<View
style={{
height: 70,
alignItems: 'center',
justifyContent: 'space-between',
marginHorizontal: 15,
flexDirection: 'row',
marginVertical: 15,
marginTop: 25,
}}>
<View
style={{
height: 40,
borderRadius: 20,
width: 300,
borderWidth: 1,
borderColor: '#F23C29',
}}>
<TextInput
placeholder="Search ..."
style={{flex: 1, paddingHorizontal: 30, color: 'white'}}
placeholderTextColor="grey"
/>
</View>
<View>
<TouchableOpacity
onPress={() => {
startRecognizing();
setShowModal(true);
}}>
<SimpleLineIcons color={'white'} name="microphone" size={30} />
</TouchableOpacity>
</View>
</View>
<SafeAreaView
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
borderColor: 'orange',
borderWidth: 1,
}}>
<FlatList
data={videoData}
// scrollEventThrottle={16}
onScroll={onScroll}
showsVerticalScrollIndicator={true}
renderItem={({item, index}) => {
return (
<View
key={index}
style={{
height: 250,
marginVertical: 20,
}}>
<Video
source={{uri: item.source}}
onLayout={onVideoLayOut}
muted={true}
resizeMode="cover"
paused={!paused}
style={{
width: '100%',
height: undefined,
aspectRatio: 2,
marginBottom: 200,
marginTop: 300,
}}
/>
<Text
style={{
fontSize: 20,
alignItems: 'center',
marginLeft: 20,
marginTop: 15,
color: 'white',
}}>
{item.title}
</Text>
</View>
);
}}
/>
</SafeAreaView>
{/* <ScrollView style={{flex: 2, width: '100%'}}>
{videoData.map((item, index) => {
return (
<View
key={index}
style={{
height: 250,
marginVertical: 20,
}}>
<Video
source={{uri: item.source}}
paused={false}
rate={1.0}
volume={1.0}
isMuted={false}
resizeMode="contain"
shouldPlay
isLooping
style={{width: '100%', height: undefined, aspectRatio: 2}}
onError={(e) => {
console.log(e, 'video data');
}}
/>
<Text
style={{
fontSize: 20,
alignItems: 'center',
marginLeft: 20,
marginTop: 15,
color: 'white',
}}>
{item.title}
</Text>
</View>
);
})}
</ScrollView> */}
<Modal
visible={showModal}
transparent={true}
onRequestClose={() => {
setShowModal(false);
}}>
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0,0,0,0.9)',
}}>
<MaterialCommunityIcons
name="microphone-settings"
size={45}
color="white"
/>
<Text
style={{
color: '#fff',
textAlign: 'center',
marginTop: 20,
fontSize: 17,
}}>
We are listening
</Text>
</View>
</Modal>
{/* <View style={{justifyContent: 'center', alignItems: 'center'}}>
<Text>Video</Text>
<View>
<TouchableHighlight
onPress={stopRecognizing}
style={styles.buttonStyle}>
<Text style={styles.buttonTextStyle}>Stop</Text>
</TouchableHighlight>
</View>
<View>
<TouchableHighlight onPress={startRecognizing}>
<SimpleLineIcons name="microphone" size={25} />
</TouchableHighlight>
</View>
{results.map((item, index) => {
return (
<Text key={`result-${index}`} style={styles.textStyle}>
{item}
</Text>
);`enter code here`
})}
<Text>Results</Text>
</View> */}
</View>
);
}
const styles = StyleSheet.create({
backgroundVideo: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
});
The question is in my flat list at a time, there are three videos which are render .. I was trying to make the first video play and rest to be in paused state and after a 5 second of interval, the next video should be played
I tried demo which looks like instagram where the video which is in focused is played automatically and rest all are paused
I tried using scroll props and also video-autoscroll lib but coudlnot resolve
the videos are getting render but
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',
}}
>