Reputation: 19341
I am trying to create object based on user input. I did following till now.
It gives me following error:
Uncaught TypeError: Cannot read property 'fullname' of undefined
const App = () => {
let passengerObj = {
"primary": {
"fullname": "",
"age": null,
},
"secondary": [],
}
const [passengerdata, setPassengerData] = useState(passengerObj)
return (
<form className="passenger-form">
<input
type="text"
placeholder="Full Name"
value={passengerdata.primary.fullname}
onChange={setPassengerData}
required/>
<input
type="number"
placeholder="Age"
value={passengerdata.primary.age || ""}
onChange={setPassengerData}
required/>
<div className="secondary-passenger-data">
<input
type="text"
placeholder="Full Name of Passenger2"
required/>
<input type="number"
placeholder="Age Passenger2"
required/>
</div>
<div className="secondary-passenger-data">
<input
type="text"
placeholder="Full Name of Passenger3"
required/>
<input type="number"
placeholder="Age Passenger3"
required/>
</div>
</form>
)}
As I am trying to add primary and secondary data in passengerObj. So, I can save it. But, It want allow me to do that.
Not sure how can I add primary and secondary data to Object using hooks.
Here sandbox I reproduce it.
https://codesandbox.io/s/distracted-sunset-89ecj?file=/src/App.js:0-1184
Any would be greatly appreciated.
Upvotes: 2
Views: 107
Reputation: 13702
Write a common onChangeHandler
which is dynamic enough to update multiple input elements.
Provide a name to your inputs. Supply a type(primary/secondary) & event to the handler.
Handler
const handleChange = (e, type) => {
const { target } = e;
setPassengerData(prev => ({
...prev,
[type]: {
...prev[type],
[target.name]: target.value
}
}));
};
JSX
<input
type="text"
placeholder="Full Name"
value={passengerdata.primary.fullname}
onChange={e => handleChange(e, "primary")}
name="fullname"
required
/>
Upvotes: 4
Reputation: 6039
What is actually happening in your code when you are entering some data for fullname
or age
the entire state object is being overwritten by the event object. So the object will not have any property called primary
so eventually when you are trying to access fullname
its giving error.
Please update your code with the below code. I have added two methods in order to update the fullname
and age
in the state.
let passengerObj = {
"primary": {
"fullname": "",
"age": null,
},
"secondary": [],
}
const [passengerdata, setPassengerData] = useState(passengerObj)
const setAge = (age) => {
setPassengerData({
...passengerdata,
primary: {
...passengerdata.primary,
"age": age
}
})
}
const setFullname = (name) => {
setPassengerData({
...passengerdata,
primary: {
...passengerdata.primary,
"fullname": name
}
})
}
return (
<form className="passenger-form">
<input
type="text"
placeholder="Full Name"
value={passengerdata.primary.fullname}
onChange={(e) => setFullname(e.target.value)}
required/>
<input
type="number"
placeholder="Age"
value={passengerdata.primary.age || ""}
onChange={(e) => setAge(e.target.value)}
required/>
<div className="secondary-passenger-data">
<input
type="text"
placeholder="Full Name of Passenger2"
required/>
<input type="number"
placeholder="Age Passenger2"
required/>
</div>
<div className="secondary-passenger-data">
<input
type="text"
placeholder="Full Name of Passenger3"
required/>
<input type="number"
placeholder="Age Passenger3"
required/>
</div>
</form>
)}
Here is another version which is generic method in order to update any input field.
let passengerObj = {
"primary": {
"fullname": "",
"age": null,
},
"secondary": [],
}
const [passengerdata, setPassengerData] = useState(passengerObj);
const setFormData = (name, value) => {
setPassengerData(passengerData => {
...passengerData,
primary: {
...passengerData.primary,
[name]: value,
}
})
}
return (
<form className="passenger-form">
<input
name="fullname"
type="text"
placeholder="Full Name"
value={passengerdata.primary.fullname}
onChange={(e) => setFormData(e.target.name, e.target.value)}
required/>
<input
name="age"
type="number"
placeholder="Age"
value={passengerdata.primary.age || ""}
onChange={(e) => setFormData(e.target.name, e.target.value)}
required/>
<div className="secondary-passenger-data">
<input
type="text"
placeholder="Full Name of Passenger2"
required/>
<input type="number"
placeholder="Age Passenger2"
required/>
</div>
<div className="secondary-passenger-data">
<input
type="text"
placeholder="Full Name of Passenger3"
required/>
<input type="number"
placeholder="Age Passenger3"
required/>
</div>
</form>
)
Hope this helps.
Upvotes: 1
Reputation: 53984
The problem is with onChange
implementation, it accepts an event
object and you trying to set the event as passengerData
.
But there is another problem when using setState
, you want to use functional updates to not get a stale state.
But, using e.target.event
in a callback will have value closure on this value, so you should use a reference with useRef
hook:
This solution won't recreate onChange
on every render (because of useCallback
and functional update of useState
), and also won't have any stale state.
const passengerObj = {
primary: {
fullname: '',
age: null
},
secondary: []
};
const App = () => {
const [passengerdata, setPassengerData] = useState(passengerObj);
const inputRef = useRef();
const onChange = useCallback(() => {
setPassengerData(prev => ({
...prev,
primary: {
...prev.primary,
fullname: inputRef.current.value
}
}));
}, []);
return (
<input
ref={inputRef}
value={passengerdata.primary.fullname}
onChange={onChange}
/>
);
};
Upvotes: 0
Reputation: 335
This maybe hepls
function App() {
let passengerObj = {
"primary": {
"fullname": "",
"age": null,
},
"secondary": [],
};
const [passengerdata, setPassengerData] = React.useState(passengerObj);
return (
<form className="passenger-form">
<input
type="text"
placeholder="Full Name"
value={passengerdata.primary.fullname}
onChange={(e) => {
const {value} = e.target;
setPassengerData((passengerdata) => ({
...passengerdata,
primary: {
...passengerdata.primary,
fullname: value
}
}));
}}
required/>
<input
type="number"
placeholder="Age"
value={passengerdata.primary.age || ""}
onChange={() => {
}}
required/>
<div className="secondary-passenger-data">
<input
type="text"
placeholder="Full Name of Passenger2"
required/>
<input type="number"
placeholder="Age Passenger2"
required/>
</div>
<div className="secondary-passenger-data">
<input
type="text"
placeholder="Full Name of Passenger3"
required/>
<input type="number"
placeholder="Age Passenger3"
required/>
</div>
</form>
);
}
note
const {value} = e.target;
Upvotes: 0