Add input data to deep object React.JS - javascript

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.

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.
Updated working demo
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
/>

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.

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;

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}
/>
);
};

Related

function to update my form makes checkboxes change from true/ false to say 'on' but need it to toggle true or false

I am having an issue where my change function makes my checkboxes go from either true/false to just be 'on'. All the fields in my form use onInputChange and they all work accept for the checkboxes. So if I type in the text fields it updates properly and saves to my database with no issue but if I click a checkbox no matter the value that it started as it will change to 'on' and gives a cannot convert varchar to int error. I am looking for a new function to update the checkboxes properly and keep the onInputChange one for the text fields.
I did create one like below and it seems to change from true to false properly but 100% clears the rest of the data already in the form.
const onCheckboxChange = e => {
setUser({
active = !active });
console.log(user)
};
import React, { useState, useEffect } from "react";
import axios from "axios";
import { useParams, Link } from "react-router-dom";
const UpdateUser = () => {
const { collectorID } = useParams();
const [user, setUser] = useState({});
const {collectorOptionsID, programBucketID,financeCompanyID, active, firstName, middleInitial, lastName, collectorCode, aging1to15, aging31to45, aging31to60, agingOver60, programBucketA, programBucketB, programBucketC, programBucketSU, financeCompany, debtType } = user;
const onInputChange = e => {
setUser({
...user, [e.target.name]: e.target.value });
console.log(user)
};
useEffect(() => {
loadUser();
}, []);// eslint-disable-line react-hooks/exhaustive-deps
const loadUser = async () => {
const result = await axios.get(`https://support.pawneeleasing.com/PublishedCollectorAPI/api/Collector/${collectorID}`);
setUser(result.data);
console.log(result.data);
};
const onSubmit = async e => {
e.preventDefault();
console.log(active);
await axios.put(process.env.NODE_ENV === 'development' ? `${process.env.REACT_APP_DEV_UPDATE}${collectorID}` : `${process.env.REACT_APP_PRO_UPDATE}${collectorID}`, {
// await axios.put(`https://support.pawneeleasing.com/PublishedCollectorAPI/api/Collector/${collectorID}`, {
collectorID: collectorID,
collectorOptionsID: collectorOptionsID,
programBucketID: programBucketID,
financeCompanyID: financeCompanyID,
active: active,
firstName: firstName,
middleInitial: middleInitial,
lastName: lastName,
collectorCode: collectorCode,
debtType: debtType,
aging1to15: aging1to15,
aging31to45: aging31to45,
aging31to60:aging31to60,
agingOver60: agingOver60,
programBucketA: programBucketA,
programBucketB: programBucketB,
programBucketC: programBucketC,
programBucketSU: programBucketSU,
financeCompany: financeCompany,
});
};
return (
<div className="newUser">
<h1>Update Collector</h1>
<form className="newUserForm" >
<div className="newUserItem">
{/*active or inactive User*/}
<label>active</label>
<div className="newUserCheckboxContainer">
<input
type='checkbox'
name="active"
defaultChecked={active}
onClick={e => onInputChange(e)}
/>
</div>
{/*Collector First Name*/}
<label>First Name</label>
<input
type="text"
name="firstName"
placeholder="First Name"
defaultValue={firstName}
onChange={e => onInputChange(e)}
/>
{/*Collector Middle Initial*/}
<label>Middle Initial</label>
<input
type="text"
name="middleInitial"
placeholder="Middle Initial"
defaultValue={middleInitial}
onChange={e => onInputChange(e)}
/>
{/*Collector Last Name*/}
<label>Last Name</label>
<input
type="text"
name="lastName"
placeholder="Last Name"
defaultValue={lastName}
onChange={e => onInputChange(e)}
/>
{/*Collector Code First Initial Middle Initial Last Initial*/}
<label>Collector Code</label>
<input
type="text"
name="collectorCode"
placeholder="Collector Code"
defaultValue={collectorCode}
onChange={e => onInputChange(e)}
/>
{/*Aging Bucket selection section */}
<label>Aging Bucket</label>
<div className='newUserCheckboxContainer'>
<label className='newUserCheckboxLabel'>1-15<br/>
<input
type='checkbox'
className='AgingBucketCheckbox'
defaultValue={aging1to15}
defaultChecked={aging1to15}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>31-45<br/>
<input
type='checkbox'
className='AgingBucketCheckbox'
defaultValue={aging31to45}
defaultChecked={aging31to45}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>31-60<br/>
<input
type='checkbox'
className='AgingBucketCheckboxsm'
defaultValue={aging31to60}
defaultChecked={aging31to60}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>Over 60<br/>
<input
type='checkbox'
className='AgingBucketCheckboxlg'
defaultValue={agingOver60}
defaultChecked={agingOver60}
onChange={e => onInputChange(e)}
/></label>
</div>
{/*Progam code selection section*/}
<label>Program Bucket</label>
<div className='newUserCheckboxContainer'>
<label className='newUserCheckboxLabel'>A<br/>
<input
type='checkbox'
className='programBucketChecbox'
defaultValue={programBucketA}
defaultChecked={programBucketA}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>B<br/>
<input
type='checkbox'
className='programBucketChecbox'
defaultValue={programBucketB}
defaultChecked={programBucketB}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>C<br/>
<input
type='checkbox'
className='programBucketChecbox'
defaultValue={programBucketC}
defaultChecked={programBucketC}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>SU<br/>
<input
type='checkbox'
className='programBucketChecbox'
defaultValue={programBucketSU}
defaultChecked={programBucketSU}
onChange={e => onInputChange(e)}
/></label>
</div>
{/*Finance Company selection section*/}
<label>Finance Company</label>
<div className='newUserCheckboxContainer'>
<input
type="text"
name="financeCompany"
placeholder="financeCompany"
defaultValue={financeCompany}
onChange={e => onInputChange(e)}
/>
</div>
<label>Debt Type</label>
<div className='newUserCheckboxContainer'>
<input
type="text"
name="debtType"
placeholder="debtType"
defaultValue={debtType}
onChange={e => onInputChange(e)}
/>
</div>
<button type='submit' onClick={(event)=>onSubmit(event)} className="userListAddButton">Update Collector</button>
<Link className="userListGoBackButton" to='/users'>Go Back</Link>
</div>
</form>
</div>
);
}
export default UpdateUser;
You need to copy the rest of user properties by rest operator
const onCheckboxChange = e => {
setUser(prev => ({
...prev,
active: !prev.active
}))
};

Setting formData with an object

Currently, i have this state with a formData.
Upon typing some text, instead to change the fullName.firstName. its making another property and just setting a single (as in single letter) value.
const [formData, setFormData] = useState({
fullName: {
firstName: "",
lastName: "",
},
email: "",
password: "",
confirmPassword: "",
});
This is how i set the formData.
const handleChange = (event) => {
const { value, name } = event.target;
console.log(name, value);
setFormData((prevFormData) => ({
...prevFormData,
[name]: value,
}));
};
This is my JSX, you may check the "name" attribute in input for some reference.
<div className="App">
<form onSubmit={onSubmit}>
<h1>Submit Form Nested Object UseState</h1>
<input
text="text"
placeholder="First Name"
name="firstName"
value={formData.fullName.firstName}
onChange={handleChange}
/>
<input
text="text"
placeholder="Last Name"
name="lastName"
value={formData.fullName.lastName}
onChange={handleChange}
/>
<input
text="email"
placeholder="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
<input
text="password"
placeholder="password"
name="password"
value={formData.password}
onChange={handleChange}
/>
<input
text="password"
placeholder="confirm Password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={handleChange}
/>
<button>Submit</button>
</form>
{JSON.stringify(formData, null, 2)}
</div>
Change the form names like so:
<form onSubmit={onSubmit}>
<h1>Submit Form Nested Object UseState</h1>
<input
placeholder="First Name"
name="fullName.firstName"
value={formData.fullName.firstName}
onChange={handleChange}
/>
<input
placeholder="Last Name"
name="fullName.lastName"
value={formData.fullName.lastName}
onChange={handleChange}
/>
<input
placeholder="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
<input
placeholder="password"
name="password"
value={formData.password}
onChange={handleChange}
/>
<input
placeholder="confirm Password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={handleChange}
/>
<button>Submit</button>
</form>
then change your handleChange function to this:
const handleChange = (event) => {
const { value } = event.target;
const [key,subkey] = event.target.name.split('.');
setFormData((prevFormData) => ({
...prevFormData,
[key]: subkey ? {
...prevFormData[key],
[subkey]: value,
} : value
}));
};

How to use the info from a component on another component

I am trying to create a payment system in my application.
I have a payment form(paymentForm.js) and it contains the payment information(cardnumber, cvv, expiry...). Is there any way that I can get these information on another component(checkoutPage.js)? Do you have any advice?
Below is my paymentForm.js:
export default class PaymentForm extends React.Component {
state = {
cvv: '',
expiry: '',
focus: '',
name: '',
number: '',
};
handleInputFocus = (e) => {
this.setState({ focus: e.target.name });
}
handleInputChange = (e) => {
const { name, value } = e.target;
this.setState({ [name]: value });
}
render() {
return (
<View>
<Cards id="PaymentForm"
cvc={this.state.cvc}
expiry={this.state.expiry}
focused={this.state.focus}
name={this.state.name}
number={this.state.number}
/>
<form style={{}}>
<input
type="tel"
name="number"
placeholder="Card Details"
maxLength="16"
preview='true'
onChange={this.handleInputChange}
onFocus={this.handleInputFocus}
/>
<br/>
<input
type="text"
name="name"
placeholder="Holder Name"
onChange={this.handleInputChange}
onFocus={this.handleInputFocus}
/>
<br/>
<input
type="tel"
name="expiry"
placeholder="Expiration"
maxLength="4"
onChange={this.handleInputChange}
onFocus={this.handleInputFocus}
/>
<br/>
<input
type="tel"
name="cvc"
placeholder="CVV"
maxLength="5"
onChange={this.handleInputChange}
onFocus={this.handleInputFocus}
/>
<br/>
<input
type="tel"
name="zipcode"
placeholder="ZIP Code"
maxLength="5"
onChange={this.handleInputChange}
onFocus={this.handleInputFocus}
/>
</form>
</View>
);
}
}
You should create a file CheckOut.js and give card information via props. There is also another way to do it by creating a class named 'Checkout' and creating static methods inside.

How to make onChange tracking function in functional component as in class component

Here is my class component
handleChange =(e) => {
this.setState({
[e.target.id]: e.target.value
})
}
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" onChange={this.handleChange}/>
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" onChange={this.handleChange}/>
</div>
As you can see we can track all the input with one function component.
But how to get the same result as that inside the functional component?
Now I am setting state for each element.
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" onChange={e =>setEmail(e.target.value)}/>
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" onChange={e => setPassword(e.target.value)}/>
</div>
The update is similar. Given some state, data for example, then a handler may look like:
const handleChange = (e) => {
const { id, value } = e.target;
setData((data) => ({
...data,
[id]: value
}));
};
Use a functional state update to spread in the existing state and use the input's id and value from the onChange event to update that section of state.
Full code example
function App() {
const [data, setData] = useState({});
const handleChange = (e) => {
const { id, value } = e.target;
setData((data) => ({
...data,
[id]: value
}));
};
return (
<div className="App">
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" onChange={handleChange} />
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" onChange={handleChange} />
</div>
<div>Email: {data.email}</div>
<div>Password: {data.password}</div>
</div>
);
}
declare one state containing both email and pasword for your functional component as below :
let [loginInfo, setLoginInfo] = useState({email:'',password:''})
handleChange =(e) => {
setLoginInfo({
...loginInfo,
[e.target.id]: e.target.value
})
}
you form
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" onChange={handleChange}/>
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" onChange={handleChange}/>
</div>
You can create object of form input and assign it to a state like below
const [formInput, setFormInput] = useState({
email: "",
password: ""
});
And set it like this
const handleChange = (e) => {
setFormInput((prevState) => ({
...prevState,
[e.target.id]: e.target.value
}));
};
See this codesandbox code
const [input, setInput] = useState({email:"", password:""})
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" value={input.email}
onChange={e => setInput({...input,[e.target.id]: e.target.value}}/>
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" value={input.password}
onChange={e => setInput({...input,[e.target.id]: e.target.value}}/>
</div>
You can try this code.
In this way you can handle both inputs using same state without handler

Not hitting `debugger` statement in `handleOnSubmit`

I'm having a pretty specific problem in my React with Rails API backend app. I have a Redux form that I am using to create a new object, a real estate listing. The state is changing in my onChange function but when I click submit, the page refreshes and nothing was created. I put debugger inside my handleOnSubmit and it’s not hitting, any ideas?
ListingForm.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { updateListingFormData } from '../actions/listingForm';
import { createListing } from '../actions/listings';
class ListingForm extends Component {
handleOnChange = event => {
const { name, value } = event.target;
const currentListingFormData = Object.assign({}, this.props.listingFormData, {
[name]: value
})
this.props.updateListingFormData(currentListingFormData)
}
handleOnSubmit = (event) => {
debugger;
event.preventDefault()
this.props.createListing(this.props.listingFormData)
}
render() {
const { title, price, location, description, img_url, agent_name, agent_number, agent_email } = this.props.listingFormData;
return (
<div className="ListingForm">
<h3>Add New Listing</h3>
<form onSubmit={(event) => this.handeOnSubmit(event)}>
<label htmlFor="listing_title">Title</label>
<input
type="text"
name="title"
value={title}
onChange={(event) => this.handleOnChange(event)}
placeholder="Listing Title"
/>
<label htmlFor="listing_location">Location</label>
<input
type="text"
name="location"
value={location}
onChange={(event) => this.handleOnChange(event)}
placeholder="Listing City or County"
/>
<label htmlFor="listing_price">Price</label>
<input
type="integer"
name="price"
value={price}
onChange={(event) => this.handleOnChange(event)}
placeholder="Listing Price"
/>
<label htmlFor="listing_description">Description</label>
<input
type="text"
name="description"
value={description}
onChange={(event) => this.handleOnChange(event)}
placeholder="Listing Description"
/>
<label htmlFor="listing_image">Attach Image</label>
<input
type="text"
name="img_url"
value={img_url}
onChange={(event) => this.handleOnChange(event)}
placeholder="Listing Image"
/>
<label htmlFor="agent_name">Listing Agent</label>
<input
type="text"
name="agent_name"
value={agent_name}
onChange={(event) => this.handleOnChange(event)}
placeholder="Agent Name"
/>
<label htmlFor="agent_number">Listing Agent Phone</label>
<input
type="text"
name="agent_number"
value={agent_number}
onChange={(event) => this.handleOnChange(event)}
placeholder="Agent Phone"
/>
<label htmlFor="agent_email">Agent Email</label>
<input
type="text"
name="agent_email"
value={agent_email}
onChange={(event) => this.handleOnChange(event)}
placeholder="Agent Email"
/>
<button type="submit"> Add Listing</button>
</form>
</div>
)
}
}
const mapStateToProps = state => {
return {
listingFormData: state.listingFormData
}
}
export default connect(mapStateToProps, {updateListingFormData,createListing})(ListingForm);
In your form onSubmit you've misspelt handleOnSubmit (you left out the l)

Categories