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}
/>
Related
I have user data fetched from API, using Redux and I want to display it once component renders and also be able to update it. Currently I have initialValues in interface as:
const initialMultisportList = [
{
multisportType: {
package: '',
type: '',
cost: 0,
},
person: {
name: '',
lastName: '',
type: '',
cooperationForm: '',
},
firmCost: 0,
comment: '',
},
{
multisportType: {
package: '',
type: '',
cost: 0,
},
person: {
name: '',
lastName: '',
type: '',
cooperationForm: '',
},
firmCost: 0,
comment: '',
},
];
const multisportList is state from Redux where data is already fetched from API, it's array of objects.
const { multisportList } = useSelector((state: RootState) => state.employeeMultisport);
This is how I assign multisportList to initialValues.
const { values, handleChange, handleSubmit } = useFormik({
initialValues: multisportList.map((person) => ({
...person,
id: Math.random(),
})),
onSubmit: (values) => {
console.log('submitted');
displayParagraphHandler();
},
});
Below is example how I render two input fields
<StyledSection>
<h3>Benefit Systems</h3>
<form onSubmit={handleSubmit}>
{values.map((person, index) => {
return (
<StyledInputRow key={index}>
<FloatingLabel text="Name" name="Name">
<StyledInputText
id="name"
name="name"
value={values[index].person.name}
onChange={handleChange}
type="text"
/>
</FloatingLabel>
<FloatingLabel text="Last Name" name="Last Name">
<StyledInputText
id="lastName"
name="lastName"
value={values[index].person.lastName}
onChange={handleChange}
type="text"
/>
</FloatingLabel>
</StyledInputRow>
</form>
</StyledSection>
The problem is that all InputFields are empty, initialState is filled with const initialMultisportList , and it's not filled by multisportList. I also cannot update it. Once component is rendered I have also replaced values.map by multisportList.map but it also didn't work.
Can You please suggest how shall I correct my code to make it work ?
thanks
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,
})
/>
)
})
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
I am using the useState hook like this:
const [formData, setFormData] = useState({
company: "",
title: "",
location: "",
from: "",
to: "",
current: false,
description: "",
});
And I have two inputs that look like this:
<div className="form-group">
<p>
<input
type="checkbox"
name="current"
checked={current}
value={current}
// use this to toggle the checkbox
onChange={(e) => {
if (!current) {
console.log("change");
setFormData({
...formData,
to: null,
company: "test company",
});
}
setFormData({ ...formData, current: !current });
toggleDisabled(!toDateDisabled);
console.log({ formData });
}}
/>{" "}
Current Job
</p>
</div>
<div className="form-group">
<h4>To Date</h4>
<input
type="date"
name="to"
value={to}
onChange={(e) => onChange(e)}
disabled={current ? "disabled" : ""}
/>
</div>
But if you look at my first input group, this part of the code is not doing anything at all:
if (!current) {
console.log("change");
setFormData({
...formData,
to: null,
company: "test company",
});
}
So, current is correctly changing, as I click the checkbox, the value of 'currentis toggling between true and false as it should. BUT, when it hits my if statement ofif(!current)` and I call setFormData, absolutely nothing happens. But if I were to add 'company: "test company" ' to the setFormData function directly below that one, the state is changing as it should.
So what is really going on here? How is my setFormData function only working outside of the if statement?
In the onChange handler of your checkbox, you should be checking the new value of the checkbox rather than the current property in the state.
And you don't really need a value defined in the checkbox since you aren't using it for anything.
In a comment you said "Basically my end goal is to change one of the values in the state to empty if the checkbox is selected".
It looks like you want to change the to property to null and the company to blank when the user unchecks the checkbox.
Here's how you can accomplish specifically that.
<input
type="checkbox"
name="current"
checked={current}
onChange={(e) => {
// If the checkbox is now checked, preserve formData.to and formData.company.
// Otherwise, overwrite to with null and company with "test company".
setFormData({
...formData,
to: e.target.checked ? formData.to : null,
company: e.target.checked ? formData.company : "test company",
current: e.target.checked,
});
}}
/>
Full Test Component
import React, { useState } from 'react';
const CheckboxTester = () => {
const [formData, setFormData] = useState({
to: "",
company: "",
current: false,
});
return (
<>
<input
type="checkbox"
name="current"
checked={formData.current}
onChange={(e) => {
setFormData({
...formData,
to: e.target.checked ? formData.to : null,
company: e.target.checked ? formData.company : "test company",
current: e.target.checked,
});
}}
/>
<hr />
<label>Current State</label>
<br />
<pre>
{JSON.stringify(formData)}
</pre>
</>
);
}
export default CheckboxTester;
It seems that your onchange function is not working correctly. You see the toggle change in UI when you click but you don't set it in the state.
Could you try to handle input change the following way?
HTML for input
<p>
<input
type="checkbox"
name="current"
checked={formData.checked}
onChange={e => this.handleInputChange(e)}
/>
Current Job
</p>
Input change handler for html
function handleInputChange(e) {
const { current } = formData;
const { checked } = e.target;
// only if current is false
if (!current) {
setFormData({
...formData,
checked,
current: !current,
});
}
}
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});
}