UseState to update multiple properties in object array - javascript

Using destructuring, I can update multiple object properties with one function:
const [serverRequest, setServerRequest] = useState({ ID: "", RAM: "", CPU: "", appInstaller: [{}] });
return (
<div className="App">
<label className="label">ID</label>
<input
className="input"
type="text"
name="ID"
value={serverRequest?.ID}
onChange={e =>
setServerRequest({
...serverRequest,
ID: e.target.value
})
}
/>
<input
className="input"
type="text"
name="RAM"
value={serverRequest?.RAM}
onChange={e =>
setServerRequest({
...serverRequest,
RAM: e.target.value
})
}
/>
</div>
);
However, I'd like to do this on a object array. How can I do this?
Using
const [serverRequests, setServerRequests] = useState([{ ID: "", RAM: "", CPU: "", appInstaller: [{}] }]);
const [selectedServer, setSelectedServer] = useState(0);
onChange={e =>
setServerRequests({
...serverRequests[selectedServer],
ID: e.target.value
})
}
Will convert the array into a single object on the first change, and then remove all other properties on the second change.

you can do something like this
onChange={e =>
setServerRequests(oldValue => {
// first spread value
oldValue[selectedServer] = {
...oldValue[selectedServer],
ID: e.target.value
}
// and now spread and return array
return [...oldValue]
})
}

onChange={e =>
setServerRequests({
...serverRequests[selectedServer],
ID: e.target.value
})
}
This means "set serverRequests with this ID, and spread serverRequests[selectedServer] inside". So yes, it becomes one single object. to avoidthat, you need to spread your previous state before:
setServerRequests({...serverRequests, otherChange: otherValue}).
Now if you need to change jsut one object from serverRequests, you need to create a copy of it, edit the object you want to change and then spread it inside your setServerRequests. Something like :
onchange = () => {
const state = Object.assign({}, serverRequests); // or lodash clonedeep
const objIndexToEdit = // get index of the item you want to changes
state[objIndexToEdit] = // your changed obj (do it as you wish, just giving an example)
setServerRequests({...state});
}

Related

change value of objects inside array of object with form input with React

Hi I'm new to react and I'm trying to make a workout tracker but I have become hardstuck on trying to capture the input of the exercises the way I'd like, as you can see by the many commented out code blocks I think that I'm missing something fundamental with useState maybe?
this is my useState
const [formData, setFormData] = useState({
title: "",
date: "",
exercises: [{ Exercise: "benchpress", Reps: "5", Sets: "5" }],
});
this works with the title and date but i've tried many approaches and cant get it work with the object inside the exercise array
this is the onChange function on the form inputs
const updateForm = (e) => {
setFormData((currentFormData) => ({
...currentFormData,
[e.target.name]: e.target.value,
}));
All the solutions I've tried has just led to adding whole new objects to the exercises array, or just adding the name + value in the original object next to the title and date.
hey try like this it will update your exercise name and you can add more input fields to update more values
import * as React from 'react'
const Comp = () => {
const [formData, setFormData] = React.useState({
title: '',
date: '',
exercises: [{ Exercise: 'benchpress', Reps: '5', Sets: '5' }]
})
React.useEffect(() => {
console.log('gg', formData)
}, [formData])
const handle = (e, type) => {
console.log('gg', e)
setFormData({
title: formData.title,
date: formData.date,
exercises: [
{
Exercise: type === 'E' ? e : formData.exercises[0].Exercise,
Reps: type === 'R' ? e : formData.exercises[0].Reps,
Sets: type === 'S' ? e : formData.exercises[0].Sets
}
]
})
}
return (
<>
<input onChange={(e) => handle(e.target.value, 'E')} />
<input placeholder="rep" onChange={(e) => handle(e.target.value, 'R')} />
<input onChange={(e) => handle(e.target.value, 'S')} />
</>
)
}
Not the best way but it works
For an array with large number of objects/feilds you should use map instead of hard coding them
setFormData(formData.map((line, i) => {
line.title= "";
line.date="",
line.exercises.map((lineExerc, j) => {
lineExerc.name = e.target.value;
});
return line;
}));

Update specific object property value in an array of objects while using inline function

My goal is to update the price field for an individual object.
Here is my selectedCurrenciesArray:
const [selectedSwapCurrencies, setSelectedSwapCurrencies] = useState([
{
symbol: null,
logo: null,
price: 0
},
{
symbol: null,
logo: null,
price: 0
},
]);
I'm mapping through an Input component like so...
{Object.values(selectedSwapCurrencies).map((currency, index) => {
return (
<Input
onChange={(e) => setSelectedSwapCurrencies({
...selectedSwapCurrencies,
[selectedSwapCurrencies[index].price]: e.target.value,
})
/>
)
})}
However, each time I update the input, it deletes the first object in the array entirely.
Try this (not tested)
selectedSwapCurrencies.map((currncy,index)=>{
return (
<Input
onChange={(e) => setSelectedSwapCurrencies({
...selectedSwapCurrencies,
[selectedSwapCurrencies[index].price]: e.target.value,
})
/>
)
})

Question on adding a input value to an array using setState

I'm trying to use setState to update a state array in React using a basic HTML input.
The state I'm trying to update looks like:
"businessName": "",
"grossAnnualSales": 0,
"contactEmail": "",
"numEmployees": 0,
"annualPayroll": 0,
"locations": [{"zip": ""}],
"industryId": "",
I need to add a zip code inputted by the user in a React component to this object in the array.
So far I've tried this and it's not working, it just updates to a string and not an array:
updateZip(){
return e => this.setState({"locations": [{"zip": e.currentTarget.value}]})
}
The React component:
<input onChange={this.updateZip()} type="text" className="zip-input"/>
How can I update the state succesfully?
Try replacing your updateZip function with an arrow function.
updateZip = (e) => {
return e => this.setState({"locations": [{"zip": e.currentTarget.value}]}) }
Also
<input onChange={(e) => this.updateZip(e)} type="text" className="zip-input"/>
use e.target.value and pass onChange={this.updateZip}
class App extends Component {
state = {
locations: [{ zip: "" }]
};
updateZip = (e) => {
this.setState({ locations: [{ zip: e.target.value }] });
};
render() {
return (
<div>
<input onChange={this.updateZip} type="text" className="zip-input" />
<p>{this.state.locations[0].zip}</p>
</div>
);
}
}
export default App;
CodeSandBox

React State with object

I am trying to update my state from my react dynamically, this is my current state.
this.state = {
title: "",
image: "",
imageFile: "",
formTitle: "",
formMessage: "",
formImage: "",
Item: {
name: "Name",
price: "",
quantity: "",
discount: "",
shipping: "",
image: "",
formType: ""
}
}
How do I update in my Textbox dynamically instead of one by one?
setItemName = (name) => {
this.setState({
Item: {
...this.state.Item,
name
}
});
};
this is my attempt at solving this
and this is my Textbox using material UI
<TextField
className="my-24"
label="Item Name"
autoFocus
id="itemName"
name="itemName"
width={1}
value={this.state.Item.name}
onChange={this.setItemName}
variant="outlined"
/>
Thanks!!
As I understand from your doubt, you want to change the fields in state dynamically instead of writing the functions one by one....
setItemName = (changedValue, type) => {
this.setState({
Item: {
...this.state.Item,
[type]: changedValue
}
});
};
And your TextField going to be...
<TextField
className="my-24"
label="Item Name"
autoFocus
id="itemName"
name="itemName"
width={1}
value={this.state.Item.name}
onChange={(changedValue) => this.setItemName(changedValue, 'name')}
variant="outlined"
/>
You just need to pass the state field name in the setItemName argument.
As for 'price' you need to write setItemName(changedValue, 'price')
And nevermind,
I solve this,
This is my code for reference:
setItemValue = (field,value) => {
console.log(field,value)
this.setState(prevState => ({
Item: { // object that we want to update
...prevState.Item, // keep all other key-value pairs
[field] : value // update the value of specific key
}
}))
setItem = (key, value) => {
this.setState({
Item: {
...this.state.Item,
[key]: value
}
});
});
You can also try the following code
handleChange = e => {
this.setState({
Item: {
...this.state.Item,
[e.target.name]: e.target.value
}
});
Use name attribute as your state key :
<TextField
rowsMax={4}
placeholder="Enter name"
value={name}
name="name"
onChange={this.handleChange}
/>

Why the code stops working when I start using localStorage?

The code below is only working when I remove the componentWillMount that uses localStorage. With usage localStorage it gives a mistake
this.state.interests.map is not a function
I tried to move usage of localStorage out of component but it won't help. I suppose that using local storage somehow changes this.state.interests that they stop being an array.
let interests = ["Музыка", "Компьютеры", "Радио"]
let ListOfInterest = React.createClass({
getInitialState: function() {
return {value: '', interests: interests};
},
componentWillMount() {
let local = localStorage.getItem('interests')
if (local) {
this.setState({interests: local});
} else {
localStorage.setItem('interests', this.state.interests)}
},
deleteInterest(key) {
delete interests[key]
this.setState(this.state) // without this line the page will not re-render
},
addInterest() {
interests.unshift(this.state.value)
this.setState({value: ''})
},
handleChange(event) {
this.setState({value: event.target.value})
},
render() {
return <div className="interests">
<b>Интересы</b>
<br/>
{this.state.interests.map((int, index) => {
return <button onClick={() => {
this.deleteInterest(index)
}} key={index} className="btn-interest">{int}</button>
})}
<input type='text' placeholder="Add" value={this.state.value} onChange={(e) => this.handleChange(e)}/>
<button onClick={() => {
this.addInterest()
}} className="add">Add interest</button>
</div>
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
You have several issues in your example
in localStorage.setItem second argument have to be a String, you can not store Array(when you do it, in storage will be string separated by coma because called method toString - [1, 2, 3].toString() ), you need stringify array before set to Storage
keyValue A DOMString containing the value you want to give the
key you are creating/updating.
localStorage.setItem(
'interests', JSON.stringify(this.state.interests)
)
and parse when get value
let local = JSON.parse(localStorage.getItem('interests'));
this.setState(this.state) this is not good way to update state, you need update state like so
deleteInterest(key) {
this.setState({
interests: this.state.interests.filter((el, i) => i !== key)
})
},
addInterest() {
this.setState({
value: '',
interests: this.state.interests.concat(this.state.value)
});
},
Example

Categories