React native specific style item in map - javascript

I have this code that generates buttons for size as come from API like (al, l, m, s ... ) and I need to put a specific style when user click in one circle, I have tried this code but it makes the same style for all circles and I need to change the style for one circle when clicked on it :
<View style={{ paddingTop: 10, width: "49%" }}>
<View style={styles.sizeView}>
<View style={styles.sizeView3}>
<Text style={styles.chartText}>{t("size")}</Text>
</View>
</View>
<View style={styles.sizeButtons}>
{this.state.productsList[0].sizes.map((item, index) => {
return (
<TouchableOpacity
key={index}
onPress={() => this.toggle1(item)}
style={
!this.state.pressStatus
? styles.sizes
: styles.sizesAlt
}
onHideUnderlay={this._onHideUnderlay.bind(this)}
onShowUnderlay={this._onShowUnderlay.bind(this)}
>
<Text
key={index}
style={
!this.state.toggle1 ? {} : { color: "#EC1C24" }
}
>
{item}
</Text>
</TouchableOpacity>
);
})}
</View>
</View>
I drag pic for the result

The issue is that you are using one variable to change the toggle status for all the buttons (this.state.toggle1), so if one is toggle they will all be toggled, as i don't think this is the intended behavior.
I suggest to create a pure component for the buttons so that each will have it's own state and handle the toggle independetly.
as for the style, they are 2 syntax working :
style={ !this.state.toggle ? {} : { color: "#EC1C24", backgroundColor: 'red' }}
or
style={[ this.state.toggle && {color: "#EC1C24", backgroundColor: 'red'} ]}
So first create a component for the circle button
export class CircleButton extends Component {
constructor(props) {
super(props);
this.state = {
toggled: false
};
}
render() {
return (
<TouchableOpacity
key={index}
onPress={() => this.toggle1(item)}
style={
!this.state.pressStatus
? styles.sizes
: styles.sizesAlt
}
onHideUnderlay={this.props.onHideUnderlay.bind(this)}
onShowUnderlay={this.props.onShowUnderlay.bind(this)}
>
<Text
key={index}
style={
!this.state.toggle ? {} : { color: "#EC1C24" }
}
>
{item}
</Text>
</TouchableOpacity>
);
}
}
And Change your View to something like this
<View style={{ paddingTop: 10, width: "49%" }}>
<View style={styles.sizeView}>
<View style={styles.sizeView3}>
<Text style={styles.chartText}>{t("size")}</Text>
</View>
</View>
<View style={styles.sizeButtons}>
{this.state.productsList[0].sizes.map((item, index) => {
return (
<CircleButton
onHideUnderlay={this._onHideUnderlay.bind(this)}
onShowUnderlay={this._onShowUnderlay.bind(this)}/>
);
})}
</View>
</View>

Related

I want to change individual element counter but when I increment the count it increments all the elements count

What I want is to increment the count for each individual item but when I'm trying to do that it changes for every element rendered. I'm getting data from API and fetching it to redux store and mapping over the array. But could not differ each element.
return (
<View style={styles.Container}>
{movies ? (
movies.map((data,i) => {
const IMAGE_URL = data.image;
return (
<Card key={i}>
<View style={styles.Content}>
<Card.Cover style={styles.image} source={{ uri: IMAGE_URL }} />
<ScrollView style={styles.ProductDetails}>
<Text
style={{
fontSize: 15,
fontWeight: "bold",
paddingBottom: 10,
}}
>
{data.title}
</Text>
<Paragraph>{data.description}</Paragraph>
</ScrollView>
</View>
<View style={styles.BottomButtonView}>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<TouchableOpacity
key={i}
style={styles.ButtonContainer}
onPress={() => setCount(count+1)}
>
<Text style={{ color: "white" }}>+</Text>
</TouchableOpacity>
<Text style={{ padding: 10 }}>{count}</Text>
<TouchableOpacity
style={styles.ButtonContainer}
onPress={() => console.log('dec')}
>
<Text style={{ color: "white" }}>-</Text>
</TouchableOpacity>
</View>
<TouchableOpacity
onPress={() => console.log("Added to Cart")}
style={styles.AddToCart}
>
<Text style={{ color: "white" }}>${data.price}</Text>
</TouchableOpacity>
</View>
</Card>
);
})
) : (
<Text>Getting Data....</Text>
)}
</View>
);
}
Updating an Item in an Array :
function updateObjectInArray(array, action) {
return array.map((item, index) => {
if (index !== action.index) {
// This isn't the item we care about - keep it as-is
return item
}
// Otherwise, this is the one we want - return an updated value
return {
...item,
...action.item
}
})
}
for Example :
const changePrice = (itemArray, item) => {
return itemArray.map(reduxItem => {
if(reduxItem.id === item.id){
reduxItem.price = reduxItem.price + reduxItem.price
}
return reduxItem
});
};

How to change change and track boolean state of each element in an array

Im trying to make it so that when a TouchableHighlight is pressed in react native it goes from one colour to another. I have state that is set to false and changes when the button is pressed. However this changes the colour for all elements in the map rather than for each item individually. Is there a way to update the state for each element independently?
Here is my code:
function OnboardingVibes() {
const [pressed, setPressed] = useState(false);
return (
<View style={{marginTop: 40}}>
<Text style={{fontSize: 22, color: '#FFF', marginBottom: 16}}>Vibes</Text>
<View style={styles.buttonContainer}>
{vibes.map((vibe) => {
return (
<TouchableHighlight onPress={() => setPressed(true)} style={{backgroundColor: pressed ? '#DDD' : '#4D2C5B', margin: 4, borderRadius: 4}} key={`vibe-${vibe}`}>
<Text style={styles.vibeButton}>{vibe}</Text>
</TouchableHighlight>
)
})}
</View>
</View>
);
}
One way to do it is to move the state down so you could have individual states. When the state is true at the top-level, all child components will receive the same state.
function TouchableVibe({vibe}) {
const [pressed, setPressed] = useState(false);
return (
<TouchableHighlight
onPress={() => setPressed(true)}
style={{
backgroundColor: pressed ? "#DDD" : "#4D2C5B",
margin: 4,
borderRadius: 4,
}}
>
<Text style={styles.vibeButton}>{vibe}</Text>
</TouchableHighlight>
);
}
function OnboardingVibes() {
return (
<View style={{ marginTop: 40 }}>
<Text style={{ fontSize: 22, color: "#FFF", marginBottom: 16 }}>
Vibes
</Text>
<View style={styles.buttonContainer}>
{vibes.map((vibe) => (
<TouchableVibe key={`vibe-${vibe}`} vibe={vibe} />
))}
</View>
</View>
);
}
const [pressed, setPressed] = useState(vibes.map(e=>false));
{vibes.map((vibe, index) => {
return (
<TouchableHighlight onPress={() => {
let new_Pressed =vibes.map(e=>false)
new_pressed[index]=true;
setPressed(new_pressed);
}}
style={{backgroundColor: pressed[index]
? '#DDD'
: '#4D2C5B',
margin: 4, borderRadius: 4}} key={`vibe-${vibe}`}>
<Text style={styles.vibeButton}>{vibe}</Text>
</TouchableHighlight>
)
})}
Instead of passing in a boolean to the state, pass in the id/vibe instead, and then in your conditional logic within the map you can determine if that single item is the same as the one you've clicked.
function OnboardingVibes() {
const [pressed, setPressed] = useState('');
return (
<View style={{marginTop: 40}}>
<Text style={{fontSize: 22, color: '#FFF', marginBottom: 16}}>Vibes</Text>
<View style={styles.buttonContainer}>
{vibes.map((vibe) => {
return (
<TouchableHighlight onPress={() => setPressed(vibe)} style={{backgroundColor: pressed === vibe ? '#DDD' : '#4D2C5B', margin: 4, borderRadius: 4}} key={`vibe-${vibe}`}>
<Text style={styles.vibeButton}>{vibe}</Text>
</TouchableHighlight>
)
})}
</View>
</View>
);
}

React Native TextInput Value persist when Tab is changed

I have encountered a weird issue in the newest react native where the value in the text input in a component remains when a tab is switched.
I can't figure what is going on and I thought it should re-render when tab is changed but it doesn't.
Here's my code
app.js
export default function App() {
const [tab, setTab] = useState("TAB1")
return (
<View style={styles.container}>
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity
style={{ borderRadius: 5, borderWidth: 1, marginRight: 5, padding: 20 }}
onPress={() => setTab("TAB1")}
>
<Text>Tab 1</Text>
</TouchableOpacity>
<TouchableOpacity
style={{ borderRadius: 5, borderWidth: 1, padding: 20}}
onPress={() => setTab("TAB2")}
>
<Text>Tab 2</Text>
</TouchableOpacity>
</View>
<View style={{ marginTop: 20}}>
{
tab === "TAB1" ? (
<View>
<InputComponent tab={tab} />
</View>
) : (
<View>
<InputComponent tab={tab} />
</View>
)
}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
marginTop: 100,
padding: 10
},
});
inputcomponent.js
export function InputComponent({ tab }) {
const [value, setValue] = useState("");
return (
<View>
<Text>{tab}</Text>
<TextInput placeholder="INPUT HERE" value={value} onChangeText={setValue}/>
</View>
)
}
It seems like the input component re-renders but the text input within it doesn't change.
Demo Issue
This is such a good question. This is because we are importing it once and passing it to two different components. It changes the tab but uses the same textinput state because they are using the same key.
To fix this pass in the key prop so React knows that tab changed:
{
tab === "TAB1" ? (
<View>
<InputComponent key={1} tab={tab} />
</View>
) : (
<View>
<InputComponent key={2} tab={tab} />
</View>
)
}
Snack: https://snack.expo.io/mVVLb9uId
Read about keys: https://reactjs.org/docs/lists-and-keys.html#keys

Automatically scroll ScrollView in KeyboardAvoidingView when I add a new TextInput

I am working with a KeyboardAvoidingView and it's working perfectly fine except for one small issue. In the code below I have a TouchableOpacity that when clicked runs the function addName() which appends to an array and creates a new TextInput - basically when you click it, it adds a new TextInput to the ScrollView.
The KeyboardAvoidingView works perfectly fine except every time a new TextInput is added/rendered, I have to scroll down to see it. Do you know how I can make it automatically scroll to the bottom when a new TextInput is rendered?
Here is my code for the KeyboardAvoidingView:
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS == "ios" ? "padding" : "height"}
>
<HeaderComponent
name={this.props.route.params.bill.barName + " Tab"}
navigation={this.props.navigation}
goHome={true}
goHomePrompt={true}
/>
<View
style={{
marginTop: 30,
marginLeft: 10,
}}
>
<Text
style={{ color: "white", fontSize: 18 }}
allowFontScaling={false}
>
Add people to split with...
</Text>
</View>
<ScrollView keyboardShouldPersistTaps={"handled"}>
{this.state.nameInput.map((value, index) => (
<View style={styles.nameContainer} key={index}>
<View
style={{
width: "90%",
}}
>
<TextInput
style={styles.textInputContainer}
value={value}
onChange={this.handleText(index)}
placeholder={"Enter name..."}
placeholderTextColor={"#333333"}
maxLength={50}
/>
</View>
<View
style={{
width: "10%",
}}
>
<TouchableOpacity
onPress={() => this.handleDelete(index)}
>
<Icon name="cancel" type="material" color="red" />
</TouchableOpacity>
</View>
</View>
))}
<TouchableOpacity onPress={() => this.addName()}>
<Text style={styles.addPerson}>+ Add Person</Text>
</TouchableOpacity>
</ScrollView>
<View style={styles.bottomContainer}>
<TouchableOpacity
style={styles.continueButton}
onPress={() => {
// filters through array to make sure there are no empty strings
let nameInput = this.state.nameInput.filter(
(name) => name !== ""
);
if (
nameInput.length > 1 &&
new Set(nameInput).size === nameInput.length
) {
this.setState({ nameInput, loading: false });
} else {
alert(
"Please make sure there are at least two people and no duplicate names!"
);
}
}}
>
<Text style={styles.continueButtonText} allowFontScaling={false}>
Continue to Split Tab
</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
And here is my code for the addName() function:
addName = () => {
let nameInput = this.state.nameInput.concat("");
this.setState({
nameInput,
});
};
This page has the solution I was looking for: Is it possible to keep a ScrollView scrolled to the bottom?

Different content on React Native based on condition

on my react-native app I would like to have premium content for people who paid a subscription.
My issue is how I make the content to display as unavailable(if you are not premium ) and the same as the other content if you are premium. Basically I would like the premium content to be displayed with a "lock overlay" on it for non-premium users.
However, I do not know how I set this conditional. It is a matter of state? If yes where should be positioned this state considering that is unidirectional?
Just to be clear I will have premium and non premium content
class Browser extends Component {
scrollX = new Animated.Value(0);
renderRecommended = () => {
return (
<View style={[styles.flex, styles.column, styles.recommended]}>
<View style={[styles.row, styles.recommendedHeader]}>
<Text
style={{
fontSize: theme.sizes.font * 1.4,
alignSelf: 'center',
color: 'white',
fontFamily: 'Nunito-Bold',
}}>
Recommended
</Text>
</View>
<View style={[styles.column, styles.recommendedList]}>
<FlatList
horizontal
scrollEnabled
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
snapToAlignment="center"
style={[styles.shadow, {overflow: 'visible'}]}
data={this.props.destinations}
keyExtractor={(item, index) => `${item.id}`}
renderItem={({item, index}) =>
this.renderRecommendation(item, index)
}
/>
</View>
</View>
);
};
renderRecommendation = (item, index) => {
const {destinations} = this.props;
const isLastItem = index === destinations.length - 1;
const {navigation} = this.props;
return (
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate('PreScreen', {
item,
})
}>
<View
style={[
styles.flex,
styles.column,
styles.recommendation,
styles.shadow,
index === 0 ? {marginLeft: theme.sizes.margin} : null,
isLastItem ? {marginRight: theme.sizes.margin / 2} : null,
]}>
<ImageBackground
style={[styles.imageback]}
source={{uri: item.preview}}
/>
</View>
<View
style={[
index === 0 ? {marginLeft: theme.sizes.margin - 10} : null,
isLastItem ? {marginRight: theme.sizes.margin / 2} : null,
]}>
<Text
style={{
fontSize: theme.sizes.font * 1.25,
fontWeight: '200',
color: 'white',
marginLeft: 10,
//paddingBottom: 20,
fontFamily: 'Nunito-Bold',
}}>
{item.title}
</Text>
<Text
style={{
color: theme.colors.caption,
marginLeft: 10,
fontFamily: 'Nunito-SemiBold',
}}>
{item.location}
</Text>
</View>
</TouchableOpacity>
);
};
render() {
return (
<ScrollView style={styles.container}>
<BackgroundSvg style={styles.background} />
<ScrollView
style={styles.contentContainer}
showsVerticalScrollIndicator={false}
contentContainerStyle={{paddingBottom: theme.sizes.paddin}}>
{this.renderRecommended()}
{this.renderRecommended2()}
{/* <View style={styles.mainContainerView}>
<TouchableOpacity style={styles.singInButton} gradient>
<Text style={styles.logInText}>
Activate premium subscription
</Text>
</TouchableOpacity>
</View> */}
</ScrollView>
</ScrollView>
);
}
}
Browser.defaultProps = {
destinations: mocks,
reading: readingList,
};
export default Browser;
My code is the one on the top. Just to simplify I am accesing some elements from JSON and I am creating Flatlist based on this. What I want is is to give some of the JSON files a bolean with premium or not and in this way to make some elements available for user or not.
As part of your destinations array, for each object, you can specify a boolean field called isPremiumItem. Your users should have a boolean field like isPremiumUser to show the type of their subscription.
Then in renderRecommended method you can check the user subscription status (isPremiumUser), and also the specific item status (isPremiumItem). Then you can render accordingly.

Categories

Resources