I am new to react world, I can't manage to change the state properly from form input field. I am building an employee profile that is going to be saved in a database. I created a profile in component state and get user data from the input field. But however, salary and headline fields are not changing while OnChange event handling function. Candidate is an object representation of employee
this.state = {
candidate: {
account: {
firstName: '',
lastName: '',
email: '',
phone: '',
},
salary: '',
headline: '',
topSkills: [{
experience1: '',
title1: ''
}, {
experience2: '',
title2: ''
}, {
experience3: '',
title3: ''
},
],
}
}
onChangefunction
handleChange(e) {
const name = e.target.name;
const value = e.target.value;
let copyState = Object.assign({},
this.state.candidate);
copyState.account[name] = value;
copyState.topSkills[name] = value;
copyState.salary = value;
copyState.headline = value;
this.setState(copyState);
}
The input field in salary and headline is not accepting input from user
<input
name="salary"
type="number"
value={this.state.candidate.salary|| ''}
onChange={this.handleChange}
/>
Can anyone provide me with help and suggest how to structure setState on onChange function?
You can simply handle changes like that for inputs:
state = {
candidate: {
account: {
firstName: '',
lastName: '',
email: '',
phone: '',
},
salary: '',
headline: '',
topSkills: [
{
experience1: '',
title1: ''
},
{
experience2: '',
title2: ''
},
{
experience3: '',
title3: ''
},
],
}
}
handleChange = (e) => {
this.setState( { candidate: { [e.target.name]: e.target.value }})
}
SetState does not required the entire object just what you are updating in the state.
Based on what you already have you could just do this
handleChange(e) {
const name = e.target.name;
const value = e.target.value;
this.setState({
account[name]: value,
topSkills[name]: value,
salary: value,
headline: value,
});
}
Though looking at your implementation, I'm not sure you will achieve what you want here... It looks like if you updated Salary, you account[name], topSkills[name], and 'headline` would be updated to the value you entered for salary.
As devserkan mentioned you can update one field at a time with setState
so what you could do is...
<input
name="salary"
type="number"
value={this.state.candidate.salary|| ''}
onChange={(e)=>this.setState({ salary: e.currentTarget.value })}/>
This is slightly inefficient because it would recreate the onChange function on every render. Your approach of creating a function outside the render in this case better...
handleSalaryChange { (e)=>this.setState({ salaray: e.currentTarget.value }); }
handleHeadlineChange { (e)=>this.setState({ headline: e.currentTarget.value }); }
render{ return (
<div>
<input
name="salary"
type="number"
value={this.state.candidate.salary|| ''}
onChange={this.handleSalaryChange)}/>
<input
name="headline"
value={this.state.candidate.headline|| ''}
onChange={this.handleHeadlineChange)}/>
...
</div>
)}
UPDATE For the handle*Change functions to work as they are currently, state would need to be updated to remove the candidate wrapper....
state = {
account: {
firstName: '',
lastName: '',
email: '',
phone: '',
},
salary: '',
headline: '',
topSkills: [
{
experience1: '',
title1: ''
},
{
experience2: '',
title2: ''
},
{
experience3: '',
title3: ''
},
],
}
Credit to udemy academy MLR — Teaching Assistant. He solved this way,the answer solve the problem.
handleChange = e => {
const candidateClone = Object.assign({}, this.state.candidate);// Shallow clone.
const accountClone = Object.assign({}, this.state.candidate.account);// Deep clone.
const topSkillsClone = Object.assign({}, this.state.candidate.topSkills);// Deep clone.
// below (let): Persists the last entered value (required).
let myHeadline = candidateClone.headline;
let myFirstName = candidateClone.account.firstName;
let mySalary = candidateClone.salary;
let myTopSkillsTitle = candidateClone.topSkills[0].title;
switch (e.target.name) {
case "headlineInput": // name in input field
myHeadline = e.target.value;
break;
case "firstNameInput": // name in input field
myFirstName = e.target.value;
break;
case "salaryInput":
mySalary = e.target.value;
break;
case "topSkillsTitleInput": // name in input field
myTopSkillsTitle = e.target.value;
break;
default:
console.log("Switch statement error");
}
accountClone.firstName = myFirstName;// Place the property value inside the deep cloned embedded object.
topSkillsClone[0].title = myTopSkillsTitle;// Place the property value inside the deep cloned embedded array.
candidateClone["account"] = accountClone;// Place the deep cloned embedded object inside the shallow cloned main object.
candidateClone["salary"] = mySalary;// Place the property inside the shallow cloned main object.
candidateClone["headline"] = myHeadline;// Place the property inside the shallow cloned main object.
candidateClone["topSkills"] = topSkillsClone;// Place the deep cloned embedded array inside the shallow cloned main object.
this.setState({candidate:candidateClone});
};
Related
I have the following code:
this.state = {
user: null
}
If the user has a name property how would I set it knowing that this.setState({user.name: 'Bob') won't work.
this.setState({ user: {...this.state.user, name: 'Bob'} });
const copy = {...this.state.user}
copy.name = 'Bob'
this.setState({ user: copy })
and if you don't want to override existing properties like say age do this
const copy = {...this.state.user}
copy.name = 'Bob'
this.setState({ user: { ...user, name: copy } })
user itself is probably an object so you can set the state of user to an object
this.setState({ user: { name: "bob" }})
or
this.state = {
user: {
name: "bob"
}
}
If your state contains an object, then you can update the value of nested state using the javascript spread operator.
Other user parameters will not be affected.
this.setState({
...this.state,
user: {
...this.state.user,
name:'Bob'
}
})
You can use spread operator for this like:
this.setState(prevState => ({
user: { ...prevState.user, name: 'Bob' }
}))
For more info:
Using State Correctly
it's my first post. I need to destructure to update a variable defined in "data", I have the following code snippets. I'm using VUE.
data: () => ({
id: '',
phone: '',
email: ''
}),
methods: {
async getId(){
{this.id, this.email, this.phone} = this.$route.query.item
}
}
Actually you can assign to existing variables.
The syntax is just a little weird.
This should work
({id: this.id, phone: this.phone, email: this.email} = this.$route.query.item)
Here's a working example
You can't destructure to existing props but to new ones only:
data () {
return {
item: {
id: '',
phone: '',
email: ''
}
}
},
...
methods: {
async getId(){
{ id, email, phone } = this.$route.query.item
Object.assign(this.item, { id, email, phone })
I am using React. This is my state
state = {
customerData: {
id: '',
name: '',
type: '',
place: '',
country: '',
timezone: 'GMT+5:30',
status: false
}
}
There is an edit functionality where the customerData object gets populated on click of edit button. I am showing this data in a modal.
Now in the modal, when I click the submit button, the modal should hide and the data populated in the customerData object should be empty. I can do it like this:
this.setState({
customerData: {
...this.state.customerData
id: '',
name: '',
type: '',
place: '',
country: '',
timezone: '',
status: false
}
}
})
I am wondering is there any one line way in ES6 to make the customerData Object empty. Because, if there are too many fields, it will be difficult.
There are two easy options here:
Create a default object
Above your component you can create the 'empty' value for your state. For example:
const emptyCustomerData = {
id: '',
name: '',
type: '',
place: '',
country: '',
timezone: '',
status: false,
}
When you want to clear your state object, now you just call:
this.setState({
customerData: emptyCustomerData,
})
Allow all the values of customerData to be nullable
You could simply set customerData to an empty object:
this.setState({
customerData: {},
})
Doing this means that before using any of the properties in your code, you need to check for undefined:
// This sets myVal to customerData.name, or if name is undefined, to an empty string
const myVal = this.state.customerData.name || ''
You can set a variable like initialState:
const initialState = {
customerData: {
id: '',
name: '',
type: '',
place: '',
country: '',
timezone: 'GMT+5:30',
status: false
}
}
And before you hide the modal, do a:
this.setState(initialState)
Assuming you want to preserve the customerData keys, for your use case it may be sufficient to set everything to null which would simplify the reset:
this.setState(customerData: Object.assign(...Object.keys(this.state.customerData).map(k => ({[k]: null}))))
https://jsfiddle.net/0ymx7fsq/
Alternatively, since you're using class components you can also save the initial state in a constructor as well:
constructor(props) {
super(props)
this.state = initialState;
}
...
reset() {
this.setState({ customerData : initialState.customerData });
}
...
You can use reduce in order not to rely on params count and number. It should looks like this
const data = {
id: '1',
name: '2',
type: '3',
place: '4',
country: '5',
timezone: '6',
status: true
}
function getDefaultObject(obj) {
const defaultValues = {
'string': '',
'boolean': false
}
return Object.keys(obj).reduce((acc, rec, index) => {
return { ...acc, [rec]: defaultValues[typeof obj[rec]]}
}, {})
}
console.log(getDefaultObject(data))
Just facing your problem today and i solved it with this code
const handleResetState = () => {
const {customerData} = this.state
let emptyState = {};
for (const key in customerData){
emptyState = {...emptyState, [key] : ""}
}
//i think you will more need the second option, so dont use first option
//normal change (first option)
this.setState({customerData: emptyState});
//in case if your key need to have special initial value that are not empty string (second option)
this.setState({customerData: {...emptyState, status: false} })
}
I have a problem in pushing input into array. I have an array with some properties and I'm going to push some value into it, but I have no idea how to tell which value is for which property.
This is my array that I want to push into it:
validInput: [{
image: avatar1,
name: '',
email: '',
passwrod: '',
phone: '',
revenue: '',
create_date: '',
age: '',
id: ''
}]
This is my function that pushes into the array:
validation(value, REGEX) {
if (REGEX.test(value) === true) {
this.state.validInput.push(value);
this.setState({
validInput: this.state.validInput
});
} else {
console.log('error');
}
}
If I understood correctly and you wish to convert your object inside validInput array into an array of objects you can do this:
Let's say we are looking to get an array of objects with the following format:
{keyName:key,keyValue:value}
we can do something like that:
const newArray = new Array();
Object.keys(this.validInput[0])
.forEach(singleKey => {
newArray.push({
keyName:singleKey,
keyValue:this.validInput[0][singleKey]
})
})
// finally - we will have the newly formatted array in newArray
I think you should have some unique way of identifying the object you want for filtering process like id, name etc. For modified function,
validation(id, value, REGEX) {
if(REGEX.test(value)){
this.state.validInput.map((user) => {
if(user.id === id) {
user.PROPERTY_YOU_NEED_TO_UPDATE = value
}
}
}
}
Since this validInput might receive another object better use to identify it using if(user.id === id). If validInput won't receive another there is no point to use array of objects.
validInput: {
image: avatar1,
name: '',
email: '',
passwrod: '',
phone: '',
revenue: '',
create_date: '',
age: '',
id: ''
}
If it's like above you can just edit the property you want...
this.setState(state => {
let user = Object.assign({}, state.validInput);
user.PROPERTY_YOU_NEED_TO_UPDATE = value;
return { user };
})
Im working on a profile editing page in an ionic app everything works as fine with all the top level items of the users profiles info (name, email, sex etc..)
I have hobbies stored in an array off this main json node (using Firestore) so its 1 level deep off the main node..
I cant seem to figure out how to use form builder with it. I suspect I am going wrong on 2 points, 1 being how I am using formbuilder and 2 on the merge fuction as it doesnt take into account nested structures which I am also unsure how to approach.. any help would be awesome.
_buildForm() {
this.form = this.formBuilder.group({
displayName: [this.options.displayName] || '',
dob: [this.options.dob] || '',
sex: [this.options.sex] || '',
city: [this.options.city] || '',
country: [this.options.country] || '',
bio: [this.options.bio] || '',
hobbies: this.formBuilder.group( this.options.hobbies )
});
// Watch the form for changes, and
this.form.valueChanges.subscribe((v) => {
this.merge(this.form.value);
});
}
merge(settings: any) {
for (let k in settings) {
this.settings[k] = settings[k];
}
return this._save();
}
_save() {
// this function then save the data back to firestore using a simple update of the entire json output
}
You need these
_buildForm() {
this.form = this.formBuilder.group({
displayName: [this.options.displayName] || '',
dob: [this.options.dob] || '',
sex: [this.options.sex] || '',
city: [this.options.city] || '',
country: [this.options.country] || '',
bio: [this.options.bio] || '',
hobbies: this.formBuilder.group([])
});
if(this.options.hobbies.length>0){
this._setHobbiesForm(this.options.hobbies);
}
}
//To build the hobby gorm
_buildHobbyForm(hobby) {
var hobbyForm = this.fb.group({
Name: hobby.Name||''
});
return hobbyForm ;
}
//To attach the hobbies form with data back to main form
_setHobbiesForm(hobbies) {
const hobbiesFGs = hobbies.map(hobby=> this._buildHobbyForm(hobby));
const hobbiesFR = this.fb.array(hobbiesFGs);
this.form.setControl('hobbies', hobbiesFR);
}
//To get form values for saving
_prepareSaveInfo(){
const formModel = this.form.value;
//deep copy of hobbies
const hobbiesDeepCopy= formModel.hobbies.map(
(hobby) => Object.assign({}, hobby)
);
const profile={
displayName: formModel.displayName as string,
sex: formModel.sex as string,
dob: formModel.dob as string,
city: formModel.city as string,
country: formModel.country as string,
hobbies:hobbiesDeepCopy
}
return profile;
}
_save() {
let dataToSave=this._prepareSaveInfo();
console.log(dataToSave);
}
This is the way to handle array inside forms in angular , if it doesn't fits your code logic exactly , take it as a example and build your logic from it , definitely this example will help you .
Iam posting an example for html also here (basically to show how to handle arrays inside the form in html)
<div formArrayName="hobbies">
<div *ngFor="let hobby of form.get('hobbies').controls; let i=index" [formGroupName]="i">
<!-- your code for hobby-->
</div>
</div>