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
}));
};
Related
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.
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
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}
/>
);
};
I'm new to react, and I've done a Form which I want to come up an alert when clicking the submit button. When I opened it on a browser through my local file it worked but when seeing on GitHub I don't get the same result, not sure why. I save it and push it to the repository.
Here it's how it looks https://joanaoli09.github.io/milestone/
Here is the code to the Form.js file:
import React from "react";
import "./Form.css";
export default class Form extends React.Component {
state = {
firstName: "",
lastName: "",
userName: "",
email: "",
password: ""
};
onSubmit = e => {
alert("Your information has been uploaded");
e.preventDefault();
this.props.onSubmit(this.state);
this.setState({
firstName: "",
lastName: "",
userName: "",
email: "",
password: ""
});
};
render() {
return (
<div className="formcontainer">
<form className="Form">
<p className="register">CREATE ACCOUNT</p>
<input
className="input"
name="firstName"
placeholder="First name"
value={this.state.firstName}
onChange={e => this.change(e)}
/>
<br />
<input
className="input"
name="lastName"
placeholder="Last name"
value={this.state.lastName}
onChange={e => this.change(e)}
/>
<br />
<input
className="input"
name="username"
placeholder="Username"
value={this.state.username}
onChange={e => this.change(e)}
/>
<br />
<input
className="input"
name="email"
placeholder="Email"
value={this.state.email}
onChange={e => this.change(e)}
/>
<br />
<input
className="input"
name="password"
type="password"
placeholder="Password"
value={this.state.password}
onChange={e => this.change(e)}
/>
<br />
<button className="submit" onClick={e => this.onSubmit(e)}>
Submit
</button>
</form>
</div>
);
}
}
If you inspect the Form.js file on that URL, you will see this:
onSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.state);
this.setState({
firstName: "",
lastName: "",
userName: "",
email: "",
password: ""
});
};
So it looks like what you have deployed to that URL and what you have posted here is not the same. You might have pushed some new code to your repo, but if the changes are not appearing on that URL, you might have forgotten to deploy them again or the deploy pipeline might have failed for some reason.
The code here is missing some bits like the onChange callback, but adding them back it works just fine:
const App = () => {
const [state, setState] = React.useState({
firstName: '',
lastName: '',
username: '',
email: '',
password: '',
});
const handleSubmit = React.useCallback((e) => {
alert('Your information has been uploaded.');
e.preventDefault();
setState({
firstName: '',
lastName: '',
username: '',
email: '',
password: '',
});
}, []);
const handleChange = React.useCallback(({ target }) => {
setState((prevState) => ({ ...prevState, [target.name]: target.value }));
}, []);
return (
<div className="formcontainer">
<form className="Form">
<p className="register">CREATE ACCOUNT</p>
<input
className="input"
name="firstName"
placeholder="First name"
value={ state.firstName }
onChange={ handleChange }
/>
<input
className="input"
name="lastName"
placeholder="Last name"
value={ state.lastName }
onChange={ handleChange }
/>
<input
className="input"
name="username"
placeholder="Username"
value={ state.username }
onChange={ handleChange }
/>
<input
className="input"
name="email"
placeholder="Email"
value={ state.email }
onChange={ handleChange }
/>
<input
className="input"
name="password"
type="password"
placeholder="Password"
value={ state.password }
onChange={ handleChange }
/>
<button className="submit" onClick={ handleSubmit }>
Submit
</button>
</form>
</div>
);
}
ReactDOM.render(<App />, document.querySelector('#app'));
body {
font-family: monospace;
}
.input,
.submit {
display: block;
margin-bottom: 8px;
font-family: monospace;
padding: 8px;
background: white;
border: 2px solid black;
}
<script src="https://unpkg.com/react#16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
I think there are some issues in your code here
<input
className="input"
name="username"
placeholder="Username"
value={this.state.username}
onChange={()=>this.change(e)}
/>
Here instead of name="username" it should be name="userName" because in
change = e => {
this.setState({
[e.target.name]: e.target.value //you are passing [e.target.name]
});};
And please make sure you always deploy the code again after making any changes in the repo by running npm run deploy
I'm new to React and have run into a problem building a simple form component. There are four input fields and four matching onChange handlers. All of them are effectively identical:
handleEmailChange(event) {
this.setState({email: event.target.value});
}
handlePasswordChange(event) {
this.setState({password: event.target.value});
}
handleFirstNameChange(event) {
this.setState({firstName: event.target.value});
}
handleLastNameChange(event) {
this.setState({lastName: event.target.value});
}
render() {
return (
<div>
<label>Email: </label>
<input type="text" name="email" placeholder="Email" value={this.state.email} onChange={this.handleEmailChange} />
<label>Password: </label>
<input type="password" name="password" placeholder="Password" value={this.state.password} onChange={this.handlePasswordChange} />
<label>First name: </label>
<input type="text" name="firstName" placeholder="First name" value={this.state.firstName} onChange={this.handleFirstNameChange} />
<label>Last name: </label>
<input type="text" name="lastName" placeholder="Last name" value={this.state.lastName} onChange={this.handleLastNameChange} />
<input type="submit" value="Add User" onClick={this.handleSubmit} />
</div>
)
}
Three of these work just fine. The password handler, however, throws a TypeError exception "event.target is undefined" when the page tries to render. If I remove the handler and input element, the app renders. If I copy and paste one of the other handlers and inputs and change the relevant names, it still throws the exception. If I change the input type from "password" to "text" it still throws the exception. I cannot figure out why a seemingly identical piece of code is throwing this exception but every other piece of code like it works just fine.
In case it matters, the code for the entire component is
class AddUserForm extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
firstName: '',
lastName: ''
};
this.validate = this.validate.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleEmailChange = this.handleEmailChange.bind(this);
this.handlePasswordChange = this.handlePasswordChange(this);
this.handleFirstNameChange = this.handleFirstNameChange.bind(this);
this.handleLastNameChange = this.handleLastNameChange.bind(this);
}
validate(user) {
return (user.email && user.password && user.firstName && user.lastName);
}
handleSubmit(event) {
let newUser = {
email: this.state.email,
password: this.state.password,
firstName: this.state.firstName,
lastName: this.state.lastName
};
if (AddUserForm.validate(newUser)) {
fetch(`http://localhost:3001/admin/addUser`, {
method: 'post',
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded',
}),
body: JSON.stringify(newUser)
})
.then( res => {
const copy = [newUser].concat(this.state.users);
this.setState({users: copy});
})
}
event.preventDefault();
}
handleEmailChange(event) {
this.setState({email: event.target.value});
}
handlePasswordChange(event) {
this.setState({password: event.target.value});
}
handleFirstNameChange(event) {
this.setState({firstName: event.target.value});
}
handleLastNameChange(event) {
this.setState({lastName: event.target.value});
}
render() {
return (
<div>
<label>Email: </label>
<input type="text" name="email" placeholder="Email" value={this.state.email} onChange={this.handleEmailChange} />
<label>Password: </label>
<input type="password" name="password" placeholder="Password" value={this.state.password} onChange={this.handlePasswordChange} />
<label>First name: </label>
<input type="text" name="firstName" placeholder="First name" value={this.state.firstName} onChange={this.handleFirstNameChange} />
<label>Last name: </label>
<input type="text" name="lastName" placeholder="Last name" value={this.state.lastName} onChange={this.handleLastNameChange} />
<input type="submit" value="Add User" onClick={this.handleSubmit} />
</div>
)
}
}
You are invoking handlePasswordChange in the constructor by writing handlePasswordChange(this). You want to bind it to this instead.
this.handlePasswordChange = this.handlePasswordChange.bind(this);