change state individually in react-native - reactjs

The data is coming dynamically and I want to change state of bookmark icon individually. When we click on first box then only change state of bookmark of that box only others will remain as it is.
My code
this.state={ isTagged: false };
_handle_tag_btn() {
if(this.state.isTagged == true) {
this.setState({isTagged: false})
}
else {
this.setState({isTagged: true})
}
}
render(){
return(
<View style={{flex: 1}}>
{
this.state.product_detail.length <= 0 ?
<ActivityIndicator color = '#bc2b78' size = "large" />
:
<View style={{flexDirection: 'row', flexWrap: 'wrap'}}>
{
this.state.product_detail.map((data, index) => (
<View style={s.cate_detail_box}>
<TouchableOpacity activeOpacity={0.9} style={{height: 190}} onPress={this._goto_individual_detail}>
<Image source={{uri: data.images[0].src}} style={s.img}/>
</TouchableOpacity>
<View style={{padding: 5}}>
<View activeOpacity={0.9} style={{flexDirection: 'row', justifyContent: 'space-between'}}>
<View>
<CapitalizedText style={s.cate_detail_title}>{data.title}</CapitalizedText>
<Text style={s.cate_detail_price}>{'₹' + data.variants[0].price}</Text>
</View>
<TouchableOpacity activeOpacity={0.7} onPress={this._handle_tag_btn} key={index}>
{
this.state.isTagged ?
<Icon size={icon_size} color="#aaa" name="turned-in-not" />
:
<Icon size={icon_size} color="#aaa" name="turned-in" />
}
</TouchableOpacity>
</View>
</View>
</View>
))
}
</View>
}
</View>
);
}
My output https://i.stack.imgur.com/05uPN.jpg
In this image all bookmarks are selected which is not required.
Any help is appreciated. Thank you.

You can take the bookmark view inside .map and create a new component and then toggle the bookmark status in that component. You pass the data attribute as a prop to this new component -
class Bookmark extends React.Component {
state = { isTagged: false }
_handle_tag_btn() {
if (this.state.isTagged == true) {
this.setState({ isTagged: false })
} else {
this.setState({ isTagged: true })
}
}
render() {
const { data } = this.props
return (
<View style={s.cate_detail_box}>
<TouchableOpacity
activeOpacity={0.9}
style={{ height: 190 }}
onPress={this._goto_individual_detail}
>
<Image source={{ uri: data.images[0].src }} style={s.img} />
</TouchableOpacity>
<View style={{ padding: 5 }}>
<View
activeOpacity={0.9}
style={{ flexDirection: "row", justifyContent: "space-between" }}
>
<View>
<CapitalizedText style={s.cate_detail_title}>
{data.title}
</CapitalizedText>
<Text style={s.cate_detail_price}>
{"₹" + data.variants[0].price}
</Text>
</View>
<TouchableOpacity
activeOpacity={0.7}
onPress={this._handle_tag_btn}
key={index}
>
{this.state.isTagged ? (
<Icon size={icon_size} color="#aaa" name="turned-in-not" />
) : (
<Icon size={icon_size} color="#aaa" name="turned-in" />
)}
</TouchableOpacity>
</View>
</View>
</View>
)
}
}
And then use it inside your .map like this -
this.state.product_detail.map((data, index) => <Bookmark data={data} />)

You could set the tagged object in state, for example:
this.state={ taggedObject: null };
_handle_tag_btn(data) {
if(this.state.taggedObject === data) {
this.setState({taggedObject: null})
}
else {
this.setState({taggedObject: data})
}
}
// ...
<TouchableOpacity activeOpacity={0.7} onPress={() => this._handle_tag_btn(data)} key={index}>
{
this.state.taggedObject === data ?
<Icon size={icon_size} color="#aaa" name="turned-in-not" />:
<Icon size={icon_size} color="#aaa" name="turned-in" />
}
</TouchableOpacity>

Related

How to make the radio button to change depending to the modal answer in react-native

I have the following code which is the main page and does have a ratio button:
<Text
style={{
fontSize: 16,
marginTop: 10,
color: "#00357B",
fontWeight: "bold",
}}
>
1. Select your recording option:
</Text>
</View>
<View>
<RadioGroup
radioButtons={radioButtons}
onPress={onPressRadioButton}
layout="row"
style={{ marginBottom: 10 }}
/>
</View>
</View>
Below in the same page I do the section of the modal where the original modal is the CategoryModal and is imported from another file:
<Modal
transparent={true}
animationType="fade"
visible={isModalVisible}
nRequestClose={() => changeModalVisible(false)}
>
<CategoryModal
changeModalVisible={changeModalVisible}
setData={setData}
headerText="Header Text"
descriptionText="Description Text"
/>
</Modal>
The Category Modal file looks like that:
closeModal = (bool, data) => {
props.changeModalVisible(bool, data);
props.setData(data);
};
return (
<TouchableOpacity disabled={true} style={styles.container}>
<View style={styles.modal}>
<View style={styles.textView}>
<Text style={[styles.text, { fontSize: 20 }]}>
{props.headerText}
</Text>
<Text style={[styles.text, { fontWeight: "light" }]}>
{props.descriptionText}
</Text>
</View>
</View>
<View style={styles.buttonsView}>
<TouchableOpacity
style={styles.touchableOpacity}
onPress={() => closeModal(false, "Cancel")}
>
<Text style={[styles.text, { color: "blue" }]}>Cancel</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.touchableOpacity}
onPress={() => closeModal(false, "Proceed")}
>
<Text style={[styles.text, { color: "blue" }]}> Proceed</Text>
</TouchableOpacity>
</View>
</TouchableOpacity>
);
};
And overall I am trying to implement the function for the ratio button to change depending to the modal answer that looks like that :
if (valueDropdown2 != null && recordedWord) {
changeModalVisible(true);
warningModalCategory = true;
const chooseData = await new Promise((resolve) => {
setData(resolve);
});
if (chooseData == "Proceed") {
setRadioButtons(radioButtonsArray);
setValueDropdown1(null);
setIsFocusDropdown1(false);
setValueDropdown2(null);
setIsFocusDropdown2(false);
radioButtonsArray.forEach((button) => {
if (button.selected) {
selectedOptionRadioButton = button.value;
}
});
} else {
radioButtonsArray.forEach((button) => {
if (button.value === "one-word") {
selectedOptionRadioButton = button.value;
}
});
}
} else {
setRadioButtons(radioButtonsArray);
setValueDropdown1(null);
setIsFocusDropdown1(false);
setValueDropdown2(null);
setIsFocusDropdown2(false);
radioButtonsArray.forEach((button) => {
if (button.selected) {
selectedOptionRadioButton = button.value;
}
});
}
}

changing the styling of one text element from inside another text element

so basically, am trying to achieve something like so, such that when +100 is pressed, the color of say, Upload ID Card changes to really show that that field is completed.
<View
style={{ justifyContent: "flex-start", marginTop: 25 }}
>
<Text
style={ styles.scoreTitle }
>
{item.key}
</Text>
</View>
<View style={styles.perScoreRightContentView}>
<Text
style={styles.scorePlus100}
onPress={() => // change style of the the text child above}
>
+ 100
</Text>
<MaterialIcons name="keyboard-arrow-right" size={24} />
</View>
</View>
So, this is what I have tried out but with no success;
...
const [isSelected, setIsSelected] = React.useState(false);
...
<Text
style={[
isSelected
? { ...styles.scoreTitle, color: "#C6C6C6" }
: styles.scoreTitle
]}
>
{item.key}
</Text>
</View>
<TouchableOpacity
style={styles.perScoreRightContentView}
onPress={() => setIsSelected(!isSelected)}
>
<Text style={styles.scorePlus100}>+ 100</Text>
<MaterialIcons name="keyboard-arrow-right" size={24} />
</TouchableOpacity>
...
const styles = StyleSheet.create({
scoreTitle: {
color: style.color.blackText,
fontSize: 13,
fontFamily: style.fontFamily.medium
}
});
Now I understand, you can put a ref on the text element and access to syle of that text element from the press function.
some thing like that :
<View style={{ justifyContent: "flex-start", marginTop: 25 }}>
<Text style={ styles.scoreTitle } ref='colorText'>
{item.key}
</Text>
</View>
<View style={styles.perScoreRightContentView}>
<Text style={styles.scorePlus100} onPress={() => changeStyle()}>
+ 100
</Text>
<MaterialIcons name="keyboard-arrow-right" size={24} />
</View>
const changeStyle = () => {
const myReference = this.colorText.current // The DOM element
myReference.style.color = "yellow";
}
don't forget to useRef to declar the ref.
I actually found a solution, thanks rovas
So basically what had to be done was to split components such that they manage different state, that is to say,
const Score = ({ title }: { title: string }) => {
const [scoreSelected, setScoreSelected] = React.useState(false);
return (
<View style={styles.perSectionScore}>
<View style={{ justifyContent: "flex-start", marginTop: 25 }}>
<Text
style={[
scoreSelected
? { ...styles.scoreTitle, color: "#C6C6C6" }
: styles.scoreTitle
]}
>
{title}
</Text>
</View>
<View
style={styles.perScoreRightContentView}
>
<Text
style={styles.scorePlus100}
onPress={() => {
setScoreSelected(!scoreSelected);
}}
>
+ 100
</Text>
<MaterialIcons name="keyboard-arrow-right" size={24} />
</View>
</View>
);
};
and then use in a to-be rendered component, i.e
<View style={styles.scoresContainer}>
<FlatList
data={scoreTitles}
renderItem={({ item }) => {
const index = scoreTitles.indexOf(item);
return (
<>
{
<View
key={index}
style={{
padding: 10
}}
>
<Score title={item.key}/>
<View style={{ flex: 0.5 }}>
<Input
disabled={true}
inputContainerStyle={{ borderBottomColor: "#EAEAEA" }}
/>
</View>
</View>
}
</>
);
}}
/>
</View>
</View>
);
...

Invariant Violation - Conditional rendering

This is driving me insane and I can't spot the issue. I'm trying to conditionally render an image component but it's throwing Invariant Violation: Text strings must be rendered within a <Text> component. Not even using a text string where the statement is.
render() {
return (
<View style={newsItemStyles.wrapper}>
{this.state.isLoading && (
<View style={loadingOverlayStyles.container}>
<ActivityIndicator />
<StatusBar barStyle="default" />
</View>
)}
{this.props.item.uri && ( // this is there the error is thrown
<View>
<Image
style={{width: '100%', height: 250}}
source={{uri: this.props.item.uri}}
/>
</View>
)}
<TouchableHighlight
onPress={() =>
this.props.navigation.navigate('NewsSingle', {
item: this.props.item,
title: this.props.item.title,
favorite: this.props.favorite,
})
}>
<Text numberOfLines={2}>{this.props.item.title}</Text>
</TouchableHighlight>
{this.props.favorite ? (
<TouchableOpacity activeOpacity={0.5} onPress={this.unfavorite}>
<FontAwesome5 name="bookmark" size={18} solid />
</TouchableOpacity>
) : (
<TouchableOpacity activeOpacity={0.5} onPress={this.favorite}>
<FontAwesome5 name="bookmark" size={18} />
</TouchableOpacity>
)}
</View>
);
}
Error is simple. You have to be careful when rendering components like:
someCondition && <someComponent />
Because there are some values that JSX will render as a text. An example of this, 0, this is a falsy value that JSX won't render as a boolean.
To fix this you just have to use the double negation operator to get the appropriate boolean value just like:
{!!this.props.item.uri && ( // error won't throw
<View>
<Image
style={{width: '100%', height: 250}}
source={{uri: this.props.item.uri}}
/>
</View>
)}
Ill modularize the code and issue should be solved otherwise letme know:
returnImage = () => (<View>
<Image
style={{width: '100%', height: 250}}
source={{uri: this.props.item.uri}}
/>
</View>)
displayImage = () => {
let renderData = this.props.item.uri ? this.returnImage():null
}
render() {
return (
<View style={newsItemStyles.wrapper}>
{this.state.isLoading && (
<View style={loadingOverlayStyles.container}>
<ActivityIndicator />
<StatusBar barStyle="default" />
</View>
)}
{}
<TouchableHighlight
onPress={() =>
this.props.navigation.navigate('NewsSingle', {
item: this.props.item,
title: this.props.item.title,
favorite: this.props.favorite,
})
}>
<Text numberOfLines={2}>{this.props.item.title}</Text>
</TouchableHighlight>
{this.props.favorite ? (
<TouchableOpacity activeOpacity={0.5} onPress={this.unfavorite}>
<FontAwesome5 name="bookmark" size={18} solid />
</TouchableOpacity>
) : (
<TouchableOpacity activeOpacity={0.5} onPress={this.favorite}>
<FontAwesome5 name="bookmark" size={18} />
</TouchableOpacity>
)}
</View>
);
}
hope it helps

How to fix JSX expression must have one parent element?

I am trying to toggle a Modal in react native. Each item in a flatlist should have a toggle option to open a modal.
I get the error:
JSX expressions must have one parent element.
I have tried to google for the right syntax but can't find a solution.
class CategoriesScreen extends Component {
state = {
modalVisible: false,
};
setModalVisible(visible) {
this.setState({ modalVisible: visible });
}
render() {
function Item({ title }) {
return (
<TouchableOpacity style={styles.item} onPress={() => {
this.setModalVisible(true);
}}>
<View>
<Text style={styles.title}>{title}</Text>
</View>
</TouchableOpacity>
<Modal
animationType="slide"
transparent={false}
visible={this.state.modalVisible}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
}}>
<View style={{ marginTop: 22 }}>
<View>
<Text>Hello World!</Text>
<TouchableOpacity
onPress={() => {
this.setModalVisible(!this.state.modalVisible);
}}>
<Text>Hide Modal</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
};
return (
<SafeAreaView style={styles.container}>
<Text style={styles.heading}>Select a category for daily tasks.</Text>
<Text style={styles.subheading}>{`You will receive a daily task in this category.\nLet’s get consistent!`}</Text>
<FlatList
data={DATA}
renderItem={({ item }) => <Item title={item.title} />}
keyExtractor={item => item.id}
numColumns={2}
/>
</SafeAreaView>
);
}
}
I am trying to get open one unique modal for each item in the flatlist.
You can only return a single entity. To fix this just surround your return in your Item function with a <Fragment/> element (from the react package).
Fragments let you group a list of children without adding extra nodes to the DOM.
This can be done like so:
import React, {Fragment} from 'react';
...
function Item({ title }) {
return (
<Fragment>
<TouchableOpacity style={styles.item} onPress={() => {
this.setModalVisible(true);
}}>
<View>
<Text style={styles.title}>{title}</Text>
</View>
</TouchableOpacity>
<Modal
animationType="slide"
transparent={false}
visible={this.state.modalVisible}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
}}>
<View style={{ marginTop: 22 }}>
<View>
<Text>Hello World!</Text>
<TouchableOpacity
onPress={() => {
this.setModalVisible(!this.state.modalVisible);
}}>
<Text>Hide Modal</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</Fragment>
)
};
Hope this helps,

Having to tap FlatList twice to activate the TouchableOpacity within the row

I have an issue with the following code. It seems that I have to tap the FlatView twice to get it to select the row. The first time it removes the keyboard and then the second time actually activates the TouchableOpacity. Can anyone see what the issue might be, Thanks.
render() {
const { navigate } = this.props.navigation;
console.log("Rendering");
if(this.state.inputText != ""){
var contactListView = <View style={styles.contactListWrapper}>
<FlatList
data = {this.state.dataContactList}
renderItem={({item}) => <TouchableOpacity onPress={() => this.pressRowContact( item )} >
<View style={styles.contactRow}>
<View style={styles.standardProfilePicWrapper}>
<Image source={require('../../images/demo/Profile/sample2.jpg')} style={{width: 30, height: 30}}/>
</View>
<Text>Someone Name Here</Text>
</View>
</TouchableOpacity>}
/>
</View>
}else {
var contactListView;
}
var selectedUsers = this.state.arrayContactsSelected.map(function(item) {
return (
<TouchableOpacity style={styles.addedContactNameWrapper} onPress={() => this.onAddedContactPress("")} key={item.userID} >
<Text style={styles.contactSelectedName} >Oliver Rice</Text>
</TouchableOpacity>
);
});
return (
<KeyboardAvoidingView behavior="padding" style={{flex: 1}}>
<View style={{flex: 1}}>
<StatusBar hidden={true} />
<View style={styles.headerBar}>
<NavBar navigation={this.props.navigation} goBack={this.goBack} title="NEW MESSAGE" backButton={true} />
</View>
<View style={styles.contentWrapper}>
<View style={styles.addContactWrapper} >
<View style={styles.addContactLeft} >
<Text style={styles.contactTo}>To:</Text>
</View>
<View style={styles.addContactRight} >
{selectedUsers}
<View style={styles.addedContactInputWrapper} >
<TextInput
style={styles.addContactInput}
onChangeText={this.onChangeTextHandler}
value={this.state.inputText}
/>
</View>
</View>
</View>
<View style={styles.mainContentWrapper} >
{contactListView}
<TextInput
style={styles.inputTextMainMessage}
/>
</View>
</View>
</View>
</KeyboardAvoidingView>
);
}
pressRowContact( rowSelected ){
var found = false;
for(var i = 0; i < this.state.arrayContactsSelected.length; i++) {
if (this.state.arrayContactsSelected[i].userID == rowSelected.userID) {
found = true;
break;
}
}
if(found == false){
var arrayContacts = this.state.arrayContactsSelected.slice()
arrayContacts.push(rowSelected)
console.log(arrayContacts)
this.setState({
inputText: "",
arrayContactsSelected: arrayContacts
});
}else {
this.setState({
inputText: "",
});
}
}

Resources