How to update the array - ReactNative - arrays

What I'm trying to achieve is an Array like this
infos = [{type: 'phone', val: '2222222'}
{type: 'email', val: 'abc#abc.com'}]
Here is the state
constructor(props) {
super(props);
this.state = {
rows: [], //this is used for the add component
contactDetails: [{type: '', val: ''}], // where i will add the values
};
}
So I have a two TextInput
<View>
<TextInput label='Type'/>
<TextInput label='Details'/>
</View>
This View can be dynamically added when a button is clicked and can also be deleted. Here is my code for the add and delete button:
addRow(){
this.state.rows.push(index++)
this.setState({ rows: this.state.rows })
}
deleteRow(key){
this.state.rows.splice(key, 1)
this.setState({ rows: this.state.rows })
}
<TouchableOpacity onPress={ this.addRow.bind(this) } >
<Text style={{textAlign: 'center',}}>Add</Text>
</TouchableOpacity>
...
<TouchableOpacity onPress={ () => this.deleteRow(i) } >
<Image source={require('./../img/delete.png')} style={{width:32, height: 32 }} />
</TouchableOpacity>
How can I get the value of it correctly and add it to my Array?
Thank you.
Update
In my TextInput I tried doing this
<TextInput
onChangeText={(text)=>{
let cd = this.state.contactDetails;
cd[i].val = text ;
this.setState({cd});
}}
value={this.state.contactDetails[i]}
label='Details'
/>
but I'm always having an error undefined is not an object (evaluating 'cd[i].val = text')

You can't manipulate state by declaring this.state.rows.push(index++), but you must do it with concat() and return the value to new variable. For instance (I actually don't know what index represents in your case, but let's give it a try:
addRow() {
const rows = this.state.rows.concat(index++)
this.setState({rows})
}
Here's a working example what I assume you're trying to achieve: https://snack.expo.io/HySVtag6g

In my state, I added an array like this
info: {
type: '',
val: '',
}
And then I added this to my addRow
addRow(){
//add new array in contactDetails
var arrayvar = this.state.contactDetails.slice()
arrayvar.push(this.state.info)
this.setState({ contactDetails: arrayvar })
//add new component row
this.state.rows.push(index++);
this.setState({ rows: this.state.rows});
}
Where every time the add button is clicked the Array named info with empty values will be added to the contactDetails and then I update the values of the added array.

Related

How to prevent the component from being repeated using array map() in react-native

I'm implementing a calendar in react native with expo, the problem is that when I want to bring a date from the firebase database to paint it in calendar, it repeats several times, here a picture
View picture
The code is this:
constructor(props) {
super(props);
this.state = {
selected: "",
usuarios: [],
};
}
componentDidMount() {
firebase
.database()
.ref("DatosCli/")
.on("child_added", (data) => {
var datos = data.val();
var usuariosTemp = this.state.usuarios;
datos.key = data.key;
usuariosTemp.push(datos);
this.setState({ usuarios: usuariosTemp });
});
}
cargarDatos = async () => {
var userTemp = new Array();
var data = await firebase.database().ref("/DatosCli").once("value");
data.forEach((child) => {
var user = child.val();
user.key = child.key;
userTemp.push(user);
});
this.setState({ usuarios: userTemp });
};
render() {
return (
<View style={styles.container}>
{this.state.usuarios.map((usuarioTemp) => (
<CalendarList
markedDates={{
[usuarioTemp.date]: {
selected: true,
disableTouchEvent: true,
selectedColor: "orange",
selectedTextColor: "red",
},
}}
/>
))}
</View>
);
}
}
I know that having the map() outside of CalendarList is the reason why it repeats itself several times, how would this be solved then?
The calendar library that i use is: https://github.com/wix/react-native-calendars
You want to have a list of marked dates and not a list of CalendarList components. Right now you are looping through the dates and create a new CalendarList for each of your dates. Instead you want to create an object of dates. Map is part of the array protoype. The prop markedDate seems to require an object though. For that, we need to refactor your code a bit and replace map by forEach for example to construct the object.
Let's split this problem up into two steps. First we create the object of markedDates by using the forEach function and after that we pass it as a prop to CalendarList.
render() {
const markedDates = {};
this.state.usuarios.forEach((usuarioTemp) => {
markedDates[usuarioTemp.date] = {
selected: true,
disableTouchEvent: true,
selectedColor: "orange",
selectedTextColor: "red",
};
};
return (
<View style={styles.container}>
<CalendarList markedDates={markedDates} />
</View>
);
}

Single selection item that saves the state (REACT-NATIVE)

I currently have 4 TouchableHighlights and my code looks as follows:
state:
this.state = {
selected: null,
selectedButton: ''
}
Touchable Highlight (they're all the same except for text)
<TouchableHighlight
onPress={this.selectedButton}
underlayColor='blue
style={[styles.box, { backgroundColor: (this.state.selected === true ? 'red' : 'blue') }]}>
<Text>1</Text>
</TouchableHighlight>
my functions:
selectedButton = () => {
this._handlePress('flag', '1')
this.setState({
selected: true,
});
};
_handlePress(flag, button) {
if (flag == 1) {
this.setState({ selected: true });
}
this.setState({ SelectedButton: button })
}
Current behaviour: Whenever I select one button, all become highlighted and cannot be unpressed.
Desired behaviour: I want only one button to be selected at the time with its state being saved somewhere.
Note: I could get the desired behaviour by creating 4 different functions that contain a different flag number, however, I'd like to get this done in the cleanest way possible.
Any guidance please?
Thank you
You can create an array of button texts, then use .map(), which provides the current index value, to iterate through them. For example:
render() {
const renderHighlight = (text, index) => (
<TouchableHighlight
onPress={() => {
if(this.state.selectedIndex === index) {
// "Unpress" functionality
this.setState({selectedIndex: undefined});
}
else {
this.setState({selectedIndex: index
}
})
style={this.state.selectedIndex === index ?
{backgroundColor: "#0F0" : {}}
>
<Text>{text}</Text>
</TouchableHighlight>;
);
const buttons = ["button 0", "button 1", "button 2"];
return buttons.map((text, i) => this.renderHighlight(text, i));
}

Proper way to push a new item onto a state object's array in react native?

I'm struggling to properly add a new element to my state object's array field:
constructor(props) {
super(props)
this.state = {
mockInterest: '',
interest: [],
testUser: new User()
}
}
addItem = () => {
this.setState({
// this adds interest to local interest array
interest: this.state.interest.concat(this.state.mockInterest)
})
}
handleClick = () => {
console.log("logged interest: " + this.state.mockInterest)
// this logs blank elemnts into the array - no good
this.setState({
testUser: {
interest_tags: [...this.state.testUser.interest_tags, this.state.interest]
}
})
console.log("New interests: " + this.state.testUser.interest_tags)
}
<View style={styles.interestInputView}>
{/* Interest Input */}
<TextInput
style={styles.interestInputText}
placeholder='Enter an Interest'
onChangeText={(text) => {
this.setState({ mockInterest: text })
}}
>
</TextInput>
<TouchableOpacity
style={styles.plusImageContainer}
onPress={this.addItem, this.handleClick}>
<Image
style={styles.plusImage}
source={require('../assets/baseline_add_black_18dp.png')}
/>
</TouchableOpacity>
</View>
Currently, I can only save the text as a mockInterest but when I want to add that to the testUser object (which has an interest_tag array field) in the state, the mockInterest isn't added to the array, interest_tags.
What am I doing wrong? I tried implementing this solution but that didn't work either.
Tyr to use prevState
this.setState(prevState => ({
testUser: {
...prevState.testUser,
interest_tags: [this.state.mockInterest, ...prevState.testUser.interest_tags]
},
}));
Note: setState is async call So you need to wait for a second before reading its value.

React-Name Array from Json with variable as key

I'm not 100% sure how to ask this question, and I'm fairly new to react-native. I have a list of categories with a count in them. In PHP I may do something like this:
$purchases['kayaks'] = 0;
$purchases['kayaks']++;
so it increments every time a kayak is sold for this example. or more specifically something like this:
$purchases[$categoryName]++;
I need to take the name of the category based on the user pressing which category they want and add a count to it, then store it in json format in AsyncStorage.
So I know I can do this:
{
"categories": [
{
"kayaks":"0"
}
]
}
And if I import that into "products" I can do products.categories.kayaks to retrieve "0" (or whatever the purchase count is), but I need kayaks to be able to be a variable based on a selection the user makes. so something more like products.categories[categoryName], or however the more optimal way to do that would be in react-native. What is the way (or if there is a more ideal way other than this) to accomplish having different category counts like this?
I hope that makes sense. Thanks in advance!
Here is a basic example written in react-native that uses AsyncStorage to read/write data and manipulates an object. Check out https://facebook.github.io/react-native/docs/asyncstorage for more info about AsyncStorage.
import React from 'react';
import { ActivityIndicator, AsyncStorage, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
input: {
minWidth: 100,
backgroundColor: 'red'
}
});
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: undefined,
categoryName: 'kayak' // default input value
};
}
componentDidMount() {
AsyncStorage.getItem('data').then(dataAsString => {
// if the data exists lets convert it into an Object we can easily edit
// else default to an empty object
this.setState({ data: dataAsString !== null ? JSON.parse(dataAsString) : {} });
});
}
handleIncrement = () => {
const { data: prevData, categoryName } = this.state;
const prevValue = prevData[categoryName];
// if the category doesn't exist the data lets default to 0
const newValue = prevValue !== undefined ? prevValue + 1 : 0;
this.setState({
data: {
...prevData, // keep all the previous data
[categoryName]: newValue // but set the selected category to the new value
}
});
};
handleSave = () => {
const { data } = this.state;
const dataAsString = JSON.stringify(data); // convert data into a string so AsyncStorage can save it properly
AsyncStorage.setItem('data', dataAsString); // save the data string
};
handleOnChangeText = categoryName => this.setState({ categoryName });
render() {
const { data, categoryName } = this.state;
return (
<View style={styles.container}>
{data === undefined ? (
<ActivityIndicator /> // While data is undefined (AsyncStorage is still doing getItem('data)) show a loading indicator
) : (
<React.Fragment>
<Text>{JSON.stringify(data, null, 2)}</Text>
<TextInput style={styles.input} value={categoryName} onChangeText={this.handleOnChangeText} />
<TouchableOpacity onPress={this.handleIncrement}>
<Text>Add/Increment</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.handleSave}>
<Text>Save</Text>
</TouchableOpacity>
</React.Fragment>
)}
</View>
);
}
}
export default App;
Note : Its best to use an object to store your data (as Cuong Tran Duc mentions) as you are indexing with category.
Hope this example was helpful.
assuse that your data get from json look like this
const data = {
"categories": [
{
"kayaks":"0"
},
{
"foo":"1"
},
{
"bar":"1"
}
]
}
const categories = data["categories"].reduce((acc, category) => (acc[Object.keys(category)[0]] = Object.values(category)[0], acc), {});
console.log(categories);
you could convert it into an object then access data by categories[categoryName] like you want
change Object.values(category)[0] to +Object.values(category)[0]
if you want to convert string to number

On an onClick method, I can't use a .push twice

I'm trying to go through a list of employees and when I click a button it adds their employee ID in an array. I try to do this with a .push method onto the array, but it only works once, and then when I try to push the button for the next employee it says .push is not a function.
constructor(props:any){
super(props);
this.state={
employees:[{name: 'joe', id: 12345},{name: 'kelly', id: 12321},{name: 'Jessica', id: 12255},{name: 'Paul', id: 98798}],
ids:[],
badgeID: '',
currentEmployee: null
}
}
check = () => {
const {ids, badgeID, employees} = this.state
let filteredEmployee = employees.filter(employee => {
return employee.id === Number(badgeID)
})
this.setState({
currentEmployee: filteredEmployee,
drinkModal: true,
ids: ids.push(badgeID)
})
console.log('ids', this.state.ids)
}
handleChange = (e: any, type: string) => {
if(type === 'badge'){
this.setState({badgeID: e.target.value})
}
}
render(){
return(
<TextField onChange={e => this.handleChange(e, 'badge')}/>
<Button onClick={this.check}>Add Employee</Button>
)}
You are doing:
ids: ids.push(badgeID)
This saves the return value of the push method which is not an array, so the next time you call push it fails. Instead you need to do something like:
ids: [...ids, badgeID]
or:
ids: ids.concat([badgeId])
This is because ids.push returns the length of the new array:
check the docs
Basically you are setting to ids an integer value.

Resources