How change the styling of dynamically generated JSX via a button click in React Native - javascript

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',
}}
>

Related

java.lang.Double cannot be cast to abie47_0_0.com.facebook.react.bridge.ReadableMap

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

React-Native expo modal with textinput, Keyboard and modal flicker after every text entry unable to input text

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

React Native keyboard disappears after each letter press

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

Issue with updating an array in React Native

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!

Make active or not active Text components

Hello I make an app in React Native !
I try to make three active or not active button. So i define a state with
const [activeLabel, setActiveLabel] = useState(0);
And on my Text compenent I have
activeLabel === 1 ? styles.selectedLabel : styles.notSelectedLabel
onPress={() => setActiveLabel(1)}
When I initialize I set activeLabel at 0 and when I click and other component with console.log activeLabel stay at 0.
I make something wrong ?
Thanks you !
Here my component Labels
function Labels({
setDataNumber,
title,
color,
firstLabel,
secondLabel,
thirdLabel,
}) {
const [activeLabel, setActiveLabel] = useState(0);
return (
<View style={{ flexDirection: "row" }}>
<Text style={{ color: color, fontSize: 20, margin: 10 }}>{title}</Text>
<View
style={{ flexDirection: "row", justifyContent: "flex-end", flex: 1 }}
>
<Text
style={{
color: color,
borderColor: color,
...(activeLabel === 0
? styles.selectedLabel
: styles.notSelectedLabel),
}}
onPress={() => setActiveLabel(0), () => setDataNumber(0)}
>
{firstLabel}
</Text>
<Text
style={{
color: color,
borderColor: color,
...(activeLabel === 1
? styles.selectedLabel
: styles.notSelectedLabel),
}}
onPress={() => setActiveLabel(1), () => setDataNumber(1)}
>
{secondLabel}
</Text>
<Text
style={{
color: color,
borderColor: color,
...(activeLabel === 2
? styles.selectedLabel
: styles.notSelectedLabel),
}}
onPress={() => setActiveLabel(2), () => setDataNumber(2)}
>
{thirdLabel}
</Text>
</View>
</View>
);
}
#sidharth-rathi offered me a solution!
function Labels({ setActiveLabel, title, color, activeIndex, labels = [] }) {
return (
<View style={{ flexDirection: "row" }}>
<Text style={{ color: color, fontSize: 20, margin: 10 }}>{title}</Text>
<View
style={{ flexDirection: "row", justifyContent: "flex-end", flex: 1 }}
>
{labels.map((el, index) => (
<Text
style={{
color: color,
borderColor: color,
...(activeIndex === index
? styles.selectedLabel
: styles.notSelectedLabel),
}}
onPress={() => setActiveLabel(index)}
>
{el}
</Text>
))}
</View>
</View>
);
}

Categories

Resources