Reputation: 123
I have a nested object as state like below -
const [userInfo, setUserInfo] = useState({
author:"",
user: {
name: 'rahul',
email: '[email protected]',
phone: [{ primary: '8888888810' }, { alternate: '7777777716' }]
}
});
I want to have 5 input fields - author, name, email, primary, and alternate and want to use only one handleChange() method to change the fields.
You can find the code I wrote on the link - https://stackblitz.com/edit/react-ngpx7q
Here, I am not able to figure out how to update the state correctly. Any help would be greatly appreciated.
Upvotes: 1
Views: 2698
Reputation: 202836
Since this was an interview question then I'd avoid 3rd-party libraries. You can use a switch
statement to handle the differently nested state, namely the name
and email
in the second level and primary
and alternate
in the third level.
const handleChange = (e) => {
const { name, value } = e.target;
switch (name) {
case "name":
case "email":
setUserInfo((userInfo) => ({
user: {
...userInfo.user,
[name]: value
}
}));
break;
case "primary":
case "alternate":
setUserInfo((userInfo) => ({
user: {
...userInfo.user,
phone: userInfo.user.phone.map((el) =>
el.hasOwnProperty(name)
? {
[name]: value
}
: el
)
}
}));
break;
default:
// ignore
}
};
Upvotes: 2
Reputation: 760
I copy paste your code and only edit your handleChange
import React, { useState } from 'react';
import './style.css';
export default function App() {
const [userInfo, setUserInfo] = useState({
user: {
name: 'ravi',
email: '[email protected]',
phone: [{ primary: '9999999990' }, { alternate: '9999998880' }]
}
});
const handleChange = e => {
console.log(e.target.name);
let arrPhone = userInfo.user.phone;
(e.target.name == 'primary' || e.target.name == 'alternate' )
&& arrPhone.map(x => (x.hasOwnProperty(e.target.name)) && (x[e.target.name] = e.target.value))
console.log(arrPhone)
setUserInfo(prevState => {
return {
user: {
...prevState.user,
[e.target.name]: e.target.value,
phone: arrPhone
}
};
});
};
const {
name,
email,
phone: [{ primary }, { alternate }]
} = userInfo.user;
console.log(userInfo);
return (
<div className="App">
Name: <input name="name" value={name} onChange={handleChange} />
<br />
Email: <input name="email" value={email} onChange={handleChange} />
<br />
Primary: <input name="primary" value={primary} onChange={handleChange} />
<br />
Alternate:{' '}
<input name="alternate" value={alternate} onChange={handleChange} />
<br />
</div>
);
}
Upvotes: 0
Reputation: 106
This works based on your original data (where phone is an array of objects):
const handleChange = e => {
let name = e.target.name;
if (['name', 'email'].includes(name)) {
setUserInfo(prevState => {
return {
user: {
...prevState.user,
[name]: e.target.value,
}
};
});
} else {
setUserInfo(prevState => {
return {
user: {
...prevState.user,
phone: name === 'primary' ?
[prevState.user.phone.find(e => Object.keys(e).includes('alternate')), {[name]: e.target.value}] :
[prevState.user.phone.find(e => Object.keys(e).includes('primary')), {[name]: e.target.value}]
}
};
});
}
};
Upvotes: 0
Reputation: 366
Instead of treating phone as object of array, which i don't think is a good idea, treat it as single object with primary and alternate as key value pairs
import React, { useState } from 'react';
import './style.css';
export default function App() {
const [userInfo, setUserInfo] = useState({
user: {
name: 'ravi',
email: '[email protected]',
phone: {
primary: 345345345345,
alternate: 234234234234
}
}
});
const handleChange = e => {
console.log(e.target.name);
setUserInfo(prevState => {
return {
user: {
...prevState.user,
[e.target.name]: e.target.value,
phone: {
...prevState.user.phone,
...{ [e.target.name]: e.target.value }
}
}
};
});
};
const {
name,
email,
phone: { primary, alternate }
} = userInfo.user;
console.log(userInfo);
return (
<div className="App">
Name: <input name="name" value={name} onChange={e => handleChange(e)} />
<br />
Email:{' '}
<input name="email" value={email} onChange={e => handleChange(e)} />
<br />
Primary:{' '}
<input name="primary" value={primary} onChange={e => handleChange(e)} />
<br />
Alternate:{' '}
<input
name="alternate"
value={alternate}
onChange={e => handleChange(e)}
/>
<br />
</div>
);
}
Upvotes: 0
Reputation: 5497
you can use lodash set to assign the value for the deeply nested object. You need to pass the path
to the name
prop of your input .
import set from 'lodash/set'
const App = () => {
const [userInfo, setUserInfo] = useState({
author:"",
user: {
name: 'rahul',
email: '[email protected]',
phone: [{ primary: '8888888810' }, { alternate: '7777777716' }]
}
});
const handleChange = (e) => {
// clone the state
const userInfoCopy = JSON.parse(JSON.stringify(userInfo));
set(userInfoCopy, e.target.name, e.target.value)
setUserInfo(userInfoCopy)
}
console.log(userInfo)
return (
<div>
<input
name="user.name"
onChange={handleChange}
/>
<input
name="user.phone.[0].primary"
onChange={handleChange}
/>
</div>
);
};
Now you can use a single handleChange
method for updating all your keys in the state .
Upvotes: 1