change state for a specific children in a loop with "useState" - javascript

I am using React native and
I have a loop in which some property types are being displayed,
i created only one state for it because property types are being fetched from DB.
What i want to do is to show them selected. kindly show me a way to achieve it.
const [propBCol, setPropBCol] = useState('#EDEDEE');
const [propTCol, setPropTCol] = useState('#000000');
{propType.map((item, index) => {
return (
<TouchableOpacity style={{
backgroundColor: propBCol,
...ListingFilterStyles.filterAnyBtn,
...ListingFilterStyles.btnMale,
}}
onPress={() => proptypes(item)}>
<Text style={{color: propTCol}}>{item.value}</Text>
</TouchableOpacity>
);
})}
the propTypes function:
const proptypes = item => {
setPropBCol('red');
setPropTCol('White')
}
enter code here
How can i change the selected item color, by above code all are item colors are being changed

const [propBCol, setPropBCol] = useState('#EDEDEE');
const [propTCol, setPropTCol] = useState('#000000');
const [selected, setSelected] = useState([]);
{propType.map((item, index) => {
return (
<TouchableOpacity style={{
backgroundColor: selected.includes(index) ? propBCol : item.propBCol,
...ListingFilterStyles.filterAnyBtn,
...ListingFilterStyles.btnMale,
}}
onPress={() => proptypes(index)}>
<Text style={{color: selected.includes(index) ? propTCol : item.propTCol}}>{item.value}</Text>
</TouchableOpacity>
);
})}
const proptypes = (index) => {
setSelected(prev => {
return [
...prev,
index
]
})
}

You should not declare a state for each one of the propType you are receiving. What you can do is, you can add a key inside the object that you are getting and saving it inside the state.
And on press you can change the value of the checked key using simple index values of the array. You have to pass the data inside the method and write the method as below
const propsTypes = (item, index) => {
var propTypesVar = [...propTypes]
propTypesVar[index].propBCol= <<Your desired color>>
propTypesVar[index].propTCol= <<Your desired color>>
setPropType(propTypesVar)
}
After that the value of propBCol will be inside the object of your propType array and you can render anything to show the user if the value is selected or not i.e.
{propType.map((item, index) => {
return (
<TouchableOpacity
style={{
backgroundColor: item.propBCol,
...ListingFilterStyles.filterAnyBtn,
...ListingFilterStyles.btnMale,
}}
onPress={() => proptypes(item)}>
<Text style={{color: item.propTCol}}>{item.value}</Text>
</TouchableOpacity>
);
})}

Related

Property not being added to object

I have a object which has an array inside it. Each array has multiple objects. So using the .map function I am looping over the array inside the object. Each array item has a click function where I toggle the item. Initial state of the array item is this
{
DisplayText: "Foresatte"
Selectable: true
UserRecipientToInclude: "IncludeGuardianRecipient"
}
When I do this
choice.Selected = false
and console logs the result the newly added item is not present in the object. Why is that?
Here is the code
{c.UserDropdownMenuChoices.map(choice => {
return ( <TouchableHighlight
{...testProperties(choice.DisplayText, true)}
style={{ opacity: !choice.Selectable ? 0.4 : 1 }}
onPress={() => {
this.selectChoice(c.UserDropdownMenuChoices, choice, c)
}}
underlayColor={'transparent'}
key={choice.DisplayText}
>
<View style={styles.choiceContainer}>
{
choice.selected ? (
//<MaterialCommunityIcons name={'checkbox-marked'} style={styles.checkboxClick} size={20} />
<Icon
type={'material-community'}
name={'checkbox-marked'}
iconStyle={styles.checkboxClick}
size={20}
/>
) : (
<Icon
type={'material-community'}
name={'checkbox-blank-outline'}
iconStyle={styles.checkboxDefault}
size={20}
/>
)
//DONE: <MaterialCommunityIcons name={'checkbox-blank-outline'} style={styles.checkboxDefault} size={20} />
}
<Text style={styles.displayText}>{choice.DisplayText}</Text>
</View>
</TouchableHighlight> )}
and my function is like this
selectChoice(choicesList, choice, contact) {
choice.selected = true
...
console.log(choice) // doesn't have the new property
}
This code is for a react native application
I have previously solved a similar issue by simply scoping out the select hook inside the .map without mapping the attribute onto the array itself.
So what I did was:
import MenuRow from "./menu_row"
...
const Rooms = allRooms.map((room, index) => {
return (
<MenuRow key={room.id} room={room}/>
)
})
Inside MenuRow.js
const MenuRow = (props) => {
let room = props.room
const [selected, setSelected] = useState(true) // Checked by default
return (
<TouchableOpacity onPress={() => {setSelected(!selected), applySelected(room.id, selected) }} style={s.checkrow}>
...
{selected ?
// SELECTED
<View style={s.checked}></View>
:
// NOT SELECTED
<View style={s.unchecked}></View>
}
</TouchableOpacity>
)
However you could also give this a try:
https://stackoverflow.com/a/44407980/4451733

Render if prop changes in React Native Function

is it possible, in a React Native Function, to render the "return" at changes?
What I try:
I have a Function - this Function gets an specific array out of another Function - on Button Press I generate a new Index - now what I want is to re-render the View to display the array element with the new Index:
const generateNewIndex = function (item) {
return Math.floor(Math.random() * item.length);
};
const PositionItem = ({ arr, position, navigation }) => {
let { name, beschreibung, arten, rooms, isStarred } = position;
let testArray = arr;
let i = 0;
return (
<View style={styles.posContainer}>
<View style={styles.titles}>
<Text style={styles.title}>{name}</Text>
<Text style={styles.subtitle}>{beschreibung}</Text>
<Text style={styles.title}>{testArray[i].name}</Text>
</View>
<View style={styles.buttonsContainer}>
<StyledButton
type="primary"
content={"Next Random Position"}
onPress={() => {
console.warn("Pressed");
i = generateNewIndex(testArray);
}}
/>
</View>
</View>
);
};
export default PositionItem;
Thanks in advance!
I have found a way which is Working.
If anyone wonders in the Future what I did:
add a Use State component:
const [count, setCount] = useState(0);
onPress on Button increase the Count:
onPress={() => {
i = generateNewIndex(array);
setCount((prevCount) => prevCount + 1);
}}

React Native Carousel OnPress

I followed this tutorial in order to have a slider with images : https://www.youtube.com/watch?v=HAgJqaJc-ck
I have three main files :
Carousel.js
const Carousel = ({data}) => {
const scrollX = new Animated.Value(0)
let position = Animated.divide(scrollX, width)
if (data && data.length){
return (
<View>
<FlatList data = {data}
keyExtractor= {(item, index) => 'key' + index}
horizontal
pagingEnabled
scrollEnabled
snapToAlignment = 'center'
scrollEventThrottle = {16}
decelerationRate = {"fast"}
showsHorizontalScrollIndicator = {false}
renderItem = {({item}) => {
return <CarouselItem item= {item} navigation={props.navigation}/>
}}
onScroll = {Animated.event(
[{nativeEvent : {contentOffset: { x : scrollX}}}]
)}
/>
<View style = {styles.dotView}>
{data.map((_, i) => {
let opacity = position.interpolate({
inputRange : [i - 1, i, i + 1],
outputRange: [0.3, 1, 0.3],
extrapolate : 'clamp'
})
return <Animated.View
key={i}
style = {{opacity, height : 10, width : 10, background: '#595959', margin: 8 , borderRadius: 5}}
/>
})}
</View>
</View>
)
}
console.log('Please provide Images')
return null
}
CarouselItem.js
const CarouselItem = ({item, navigation}) => {
const url = item.url
return (
<View style={styles.cardView} >
<TouchableOpacity onPress={() => props.navigation.navigate("BoatInfo")}><Image style={styles.image} source={{uri: item.url}} /></TouchableOpacity>
<View style={styles.textView}>
<Text style={styles.itemTitle}>{item.title}</Text>
<Text style={styles.imageDescription}>{item.description}</Text>
</View>
</View>
)
}
And Data.js
export const dummyData = [
{
title: 'BOAT DETAILS', url : require('../component/schema.jpg'),
description: 'PDF containing the specificities of the boat', id : 1
},
{
title: 'TUTORIALS', url : require('../component/tuto.png'),
description: 'Become a capitain by watching these videos', id : 2
},
{
title: 'YOUR TRIP', url : require('../component/trip.png'),
description: 'Follow your trip details', id : 3
},
]
Now, As you see in CarouselItem, I am trying to add a functionality so that when I press on the image, it would take me to another page (each picture takes me to a different page). However, when I try to do so, I get the following error:
So, I understand that in props it is empty...
But when I switch const CarouselItem = ({ item }, props) => { to const CarouselItem = ( props, { item }) => { Then I get the following error:
What I don't understand is that when I remove {item} then the navigation works and when I remove props then {item} works... How can both work? What am I doing wrong ?
Only screens in React Native automatically have access to the navigation prop, so you'll need to pass it to the CarouselItem component, then use the always fun object destructuring to get your particular props in the component (like you did with item). It should look like this:
...
renderItem = {({item}) => {
return <CarouselItem item={item} navigation={navigation}/>
}}
...
and then:
const CarouselItem = ({ item, navigation }) => {
...
and that should work!
Good luck!
You have to pass navigation prop object from flatlist with item object too something like this
navigate ={props.navigation} item={item}
in crousal function you have to write like this
crousalItem =(item, navigation) =>
This way you got both item and navigation object.
use item to list data, navigation.navigate() to move to another screen

Reset state after unmount screen - Hooks?

After getting data from API I set it to state, and render items in Flatlist,
when I select any item from it I manipulate data and add a new property to item object named as "toggle: true"
and it's works well when I select any item from list I add a border based on toggle,
But when I go back to previous screen then re open the lists screen I can see the border rendered around the items, although I reset the state when the unmounted screen
So what's the wrong I made here?
Code snippet
Data
export default {
...
services: [
{
id: 0,
name: 'nameS0',
logo:
'https://cdn2.iconfinder.com/data/icons/hotel-98/64/hair-dryer-tools-beauty-hairdressing-512.png',
price: 19.99,
},
],
employees: [
{
id: 0,
name: 'name0',
img:
'https://www.visualelementmedia.com/wp-content/uploads/2015/04/person-4-400x629.jpg',
},
...
],
};
const VendorProfile = ({navigation}) => {
const [services, setServices] = React.useState(null);
const [employees, setEmployees] = React.useState(null);
const [serviceSelected, setServiceSelected] = React.useState(null);
const [employeeSelected, setEmployeeSelected] = React.useState(null);
// For selected Item (services, employees)
const itemSelected = (data, id) => {
const updated = data.map((item) => {
item.toggle = false;
if (item.id === id) {
item.toggle = true;
data === services
? setServiceSelected(item)
: setEmployeeSelected(item);
}
return item;
});
data === services ? setServices(updated) : setEmployees(updated);
};
...
const renderEmployees = ({item}) => {
return (
<TouchableOpacity
onPress={() => itemSelected(employees, item.id)}
delayPressIn={0}
style={styles.employeeContainer}>
<EmployeePattern style={{alignSelf: 'center'}} />
<View style={styles.employeeLogo}>
<Image
source={{uri: item.img}}
style={[styles.imgStyle, {borderRadius: 25}]}
/>
</View>
<View style={{marginTop: 30}}>
<Text style={{textAlign: 'center'}}> {item.name}</Text>
</View>
<View style={{marginTop: 10, alignSelf: 'center'}}>
{item.toggle && <AntDesign name="check" size={25} color="#000" />} // here it's stuck after back and reopen the screen
</View>
</TouchableOpacity>
);
};
React.useEffect(() => {
setServices(VendorProfileData.services);
setEmployees(VendorProfileData.employees);
() => {
setServices(null);
setEmployees(null);
};
}, []);
return (
<View style={styles.container}>
<FlatList
data={services}
renderItem={renderServices}
horizontal
keyExtractor={(item) => item.id.toString()}
contentContainerStyle={{
justifyContent: 'space-between',
flexGrow: 1,
}}
/>
.....
</View>
);
};
Ok so after trying multiple times, i got it
change this
const updated = data.map((item) => {
to this
const updated = data.map((old) => {
let item = {...old};
and please make sure everything is working and we didn't break a thing :),
On your ItemSelected function you are passing the whole employees list, and going through it now thats fine, but when you changing one item inside this list without "recreating it" the reference to that item is still the same "because its an object" meaning that we are modifying the original item, and since we are doing so, the item keeps its old reference, best way to avoid that is to recreate the object,
hope this gives you an idea.

React Native: In an array of Text Inputs which are looped then displayed, how to get nth element and modify those elements separately?

In this module I am trying to create a survey module similar to the one in twitter.
at first, color of text input borders are grey and when I focus (click) the text input, only one of them (clicked one) must be blue. Same idea when I type a text, they all shouldn't get the same value. I should be able to get each text input value that I created by clicking plus icon, as a String
Should I use a flatlist or a listview rather than a for loop ?
React-Native Listview, press row and change that row style
I also tried to solve it according to this example.
I change this example a little bit, I was able to change border color of clicked one. but still, I couldn't get the values...
Any solution suggestion ? Thank you.
screenshot 1
screenshot 2
This is my code;
changeInputBorderColor = () => {
const newinputBorderColor = cloneDeep(this.state.inputBorderColor);
newinputBorderColor.bar = '#04A5F5';
this.setState({inputBorderColor: {bar: newinputBorderColor.bar}});
};
changeInputBorderColor2 = () => {
this.setState({
inputBorderColor: {
bar: 'grey'
}
})
};
incrementInputCount = () => {
if (this.state.inputCounter < 5) {
this.setState(prevState => {
return {inputCounter: prevState.inputCounter + 1}
});
console.log(this.state.inputCounter);
}
else {
this.setState(prevState => {
return {inputCounter: prevState.inputCounter}
});
alert("Maximum soru sayısına ulaştınız");
}
};
render() {
let surveyOptions = [];
for (let i = 0; i < this.state.inputCounter; i++) {
console.log(this.state.inputCounter);
surveyOptions.push(
<View key={i}>
<View>
<TextInput
style={[styles._surveyTextInput, {borderColor: this.state.inputBorderColor.bar}]}
onChangeText={(text) => this.setState({text})}
value={this.state.text}
onFocus={this.changeInputBorderColor}
onBlur={this.changeInputBorderColor2}
placeholder={"Secenek " + (i + 1)}
/>
</View>
</View>
)
}
return (
<View style={styles._surveyMainContainer}>
<View style={{flex: 0.8}}>
{surveyOptions}
<TouchableOpacity style={{position: 'absolute', right: 5, top: 5}}>
<Ionicons name={"ios-close-circle"}
size={30}
color={'black'}
/>
</TouchableOpacity>
<TouchableOpacity style={{position: 'absolute', right: 5, top: 45}}
onPress={this.incrementInputCount}>
<Ionicons name={"ios-add-circle"}
size={30}
color={'blue'}
/>
</TouchableOpacity>
</View>
<View style={{flex: 0.2}}>
<View
style={styles.renderSeparator}
/>
<Text style={{fontWeight: 'bold', margin: 5}}>Anket süresi</Text>
</View>
</View>
);
}
You can do it with a .map however you have to set it up correctly so that each TextInput has its own value in state. Currently what you are doing is setting the same value in state for each TextInput this results in every TextInput having the same value. Clearly not what you want.
Create an initial array in state (textArray) that has all values as empty strings, this will be used to store the values from each TextInput.
Set the focusedIndex to be null in state
Create a function that uses the previous state value to update the current state.
Create a function to handle the changing of the box color, it will just compare the TextInput index with the current focusedIndex
Iterate over the textArray and create the TextInput components. Make sure each TextInput has its own value in state.
Make sure we set the value of the focusedIndex in the onFocus and onBlur in the TextInput. When it blurs we should set the value to null so that it removes the border color when the keyboard is dismissed.
So we could do something like the following
export default class App extends React.Component {
constructor(props) {
super(props);
// construct an array with the number of textInputs we require,
// each value an empty string
// set this array in state
// set the focusedIndex to null
let textArray = Array(6).fill('');
this.state = {
textArray: textArray,
focusedIndex: null
}
}
// this function will handle setting of the state when each TextInput changes
onChangeText = (text, index) => {
// as there are going to be a lot of setState calls
// we need access the prevState before we set the next state.
this.setState(prevState => {
prevState.textArray[index] = text
return {
textArray: prevState.textArray
}
}, () => console.log(this.state.textArray))
}
// handle the border color
handleBorderColor = (index) => {
return index === this.state.focusedIndex ? 'red' : 'grey'
}
render() {
// here we map the items in the `this.state.textArray`
// notice that each TextInput is give a specific value in state
// that will stop the overlap
return (
<View style={styles.container}>
{this.state.textArray.map((text, index) => {
return <TextInput
style={{height: 40, marginVertical: 10, borderColor: this.handleBorderColor(index), borderWidth: 1}}
onChangeText={text => this.onChangeText(text, index)}
value={this.state.textArray[index]}
placeholder={`placeholder for ${index}`}
onFocus={() => this.setState({focusedIndex: index})}
onBlur={() => this.setState({focusedIndex: null})}
/>
})}
</View>
);
}
}
If you then want to access a specific value for a TextInput you can do so like this
let value = this.state.textArray[index]; // where the index is the value you want
Here is an example snack showing the code working
https://snack.expo.io/#andypandy/map-multiple-textinputs
It is definitely worthwhile looking at the following articles about state, as I have used these properties in this example.
https://medium.learnreact.com/setstate-is-asynchronous-52ead919a3f0
https://medium.learnreact.com/setstate-takes-a-callback-1f71ad5d2296
https://medium.learnreact.com/setstate-takes-a-function-56eb940f84b6

Categories