Parent Component
I have a parent component Users with the following snippet:
addUser(index, user) {
var users = this.state.users
var existingUser = users[index]
if (existingUser !== undefined) {
this.updateUser(index, user)
} else {
users.push(user)
this.setState({
users : users
})
}
}
updateUser(index, itemAttributes) {
this.setState({
travellers: [
...this.state.users.slice(0,index),
Object.assign({}, this.state.users[index], itemAttributes),
...this.state.users.slice(index+1)
]
});
}
The updateUser functionality has been taken from React: Updating state when state is an array of objects
And in JSX I have the following snippet:
...
<Form onSubmit={this._handleFormSubmit.bind(this)}>
<UserDetails index={0} callback = {this.addUser.bind(this)}/>
<UserDetails index={1} callback = {this.addUser.bind(this)}/>
<input type="submit" className="btn btn-primary mb-4" value="Continue"/>
</Form>
...
Child Component
Then in the child component UserDetails I have:
handleChange(event) {
event.preventDefault();
let formValues = this.state.formValues;
let name = event.target.name;
let value = event.target.value;
formValues[name] = value;
this.setState({formValues})
this.props.callback(this.props.index, this.state.formValues)
}
And in JSX:
<div className="row">
<div className="col-md-4">
<FormGroup>
<Label>First Name</Label>
<Input type="text" name="firstName" placeholder="First Name" value={this.state.formValues["firstName"]} onChange={this.handleChange.bind(this)} />
</FormGroup>
</div>
<div className="col-md-4">
<FormGroup>
<Label>Last Name</Label>
<Input type="text" name="lastName" placeholder="Last Name" value={this.state.formValues["lastName"]} onChange={this.handleChange.bind(this)} />
</FormGroup>
</div>
</div>
Now I can see two forms, each with a First Name and a Last Name fields. Now the problem is when I enter the First Name for the first user, the First Name for the second user is also automatically set to that of the first user. Thus, I cannot enter separate names for the two users. How can I solve this issue?
I see that you are mutating the state directly by using
let formValues = this.state.formValues;
formValues[name] = value;
One way to alleviate this issue is to spread the formValues before assigning
let formValues = {...this.state.formValues};
or you can use Immer
Related
I have a signup page which has 4 input text fields which changes the currentState of each field from an empty string to whatever I inputted. After signing up it logs each of the following 4 fields input in a database with a post request. How can I clear each field after clicking on the sign up button. Basically I just want the page to clear after clicking. I have attempted to set the state of the 4 variables back to an empty string at the end of my promise chain but nothing changes still.
import React,{useState} from 'react';
import style from './Signup.css';
import Axios from 'axios';
function Signup() {
const [firstReg, setFirstNameReg] = useState('')
const [lastReg, setLastNameReg] = useState('')
const [emailReg, setEmailReg] = useState('')
const [passReg, setPassReg] = useState('')
const register = () => {
Axios.post('http://localhost:3001/register', {
first_name: firstReg,
last_name: lastReg,
email: emailReg,
password: passReg,
}).then((response)=> {
console.log(response);
setFirstNameReg('')
});
};
return (
<div className="Signup">
<div className='Sign'>
<div class="photo"> Create an account</div>
<div class ='searche'>
<div className="searchInputse">
<input type="text" onChange={(e)=> {setFirstNameReg(e.target.value)}} placeholder={'First name'} />
<div className="searchIcone"></div>
</div>
<div className="dataResult"></div>
</div>
<div class ='searche'>
<div className="searchInputse">
<input type="text" onChange={(e)=> {setLastNameReg(e.target.value)}} placeholder={'Last name'}/>
<div className="searchIcone"></div>
</div>
<div className="dataResult"></div>
</div>
<div class ='searche'>
<div className="searchInputse">
<input type="text" onChange={(e)=> {setEmailReg(e.target.value)}} placeholder={'Email'}/>
<div className="searchIcone"></div>
</div>
<div className="dataResult"></div>
</div>
<div class ='searche'>
<div className="searchInputse">
<input type="text" onChange={(e)=> {setPassReg(e.target.value)}} placeholder={'Password'}/>
<div className="searchIcone"></div>
</div>
<div className="dataResult"></div>
</div>
<button className='searchee' onClick={register}>
Sign Up
</button>
</div>
</div>
);
}
export default Signup;
A few things you should be aware of.
1st. You want to keep your data in an object for easy management and code reduction like so:
Define the initial object outside of your component;
let initialValues = {
'first_name': '',
'last_name': '',
'email': '',
}
And inside your component define state with the initialValues variable as the state default.
const [data, setData] = useState(initialValues);
And then, in your JSX you can connect the values with the object keys like so:
<input type="text" name="first_name" value={data.first_name} />
<input type="text" name="last_name" value={data.last_name} />
<input type="text" name="email" value={data.email} />
You can also then add an onChange handler to that input like so:
note: name must match the key inside of the initalValues object.
<input type="text" name="first_name" value={data.first_name} onChange={handleChange} />
<input type="text" name="last_name" value={data.last_name} onChange={handleChange} />
<input type="text" name="email" value={data.email} onChange={handleChange} />
handleChange basic function can look like this:
const onChange = (e) => {
setData({...data, [e.target.name]: e.target.value})
}
Essentially all you're doing is typing inside the input field, onChange detects a change on each key press, and fires off the handleChange function, that function makes a copy of the current state of data then looks at the e.target.name which could be first_name and sets it to e.target.value which is anything you type.
That way, all you need to pass to axios is the data object.
I hope this helps, let me know if you have any other questions.
Happy coding!
Hi try to replace your register button with this :
<button className='searchee' onClick={()=>{register();setFirstNameReg('');setLastNameReg('');setEmailReg('');setPassReg('')}}>
Sign Up
</button>
I am trying to implement a simple form validation in React where the length of the name should be more than 1 char long, length of country should be more than 2 char long along with the password. However, the password and the country are getting validated properly. But, the name field isn't getting properly validated. Firstly, it's allowing to submit the name if it's just 1 char long and instead is showing the error in the country's span tag. Also, I am not sure how to implement the logic for email validation. Here is my code:
import React, { Component } from "react";
export default class SignUp extends Component {
constructor(){
super();
this.state={
name:'',
country:'',
email:'',
pass:'',
formErrors:{
nameError:'',
countryError:'',
emailError:'',
passwordError:''
}
}
this.handleChange=this.handleChange.bind(this);
this.handleValidate=this.handleValidate.bind(this);
this.handleSubmit=this.handleSubmit.bind(this);
}
handleChange =(e) =>{
let name=e.target.name;
let value=e.target.value;
this.setState({
[name]:value
})
}
handleValidate= () =>{
let { name,country,email,pass} =this.state;
let nameError, countryError, emailError, passError;
if(!name)
nameError="Missing name"
if(name && name.length<2)
countryError="Length of name should be more than 1 character"
if(!country)
countryError="Missing country"
if(country && country.length<3)
countryError="Length of country should be more than 2 characters"
/* if(!email)
emailError="Email can't be empty"
let lastAtPosi=email.lastIndexOf('#');
let lastDotPosi=email.lastIndexOf('.');
console.log("last # posti"+lastAtPosi);
console.log("Last . posi"+lastDotPosi);
*/
if(!pass)
passError="Password can't be empty"
if(pass && pass.length<6)
passError="Password must be more than 6 characters long"
this.setState({
formErrors:{
nameError:nameError,
countryError:countryError,
// emailError:emailError,
passwordError:passError
}
})
console.log("name "+nameError);
}
handleSubmit= (e) =>{
e.preventDefault();
this.handleValidate();
}
render() {
const { name, country, email, pass, formErrors } = this.state;
return (
<div>
<form>
<h3>Sign Up</h3>
<div className="form-group">
<label>Name</label>
<input type="text" onChange={this.handleChange}name="name"value={name} className="form-control" placeholder="Enter name" />
{formErrors.nameError? <span variant="danger">{formErrors.nameError}</span>:'valid'}
</div>
<div className="form-group">
<label>Country</label>
<input type="text" onChange={this.handleChange}name="country"value={country} className="form-control" placeholder="Enter country" />
{formErrors.countryError? <span variant="danger">{formErrors.countryError}</span>:'valid'}
</div>
<div className="form-group">
<label>Email address</label>
<input type="email" onChange={this.handleChange}name="email"value={email} className="form-control" placeholder="Enter email" />
{formErrors.emailError?<span variant="danger">{formErrors.emailError}</span>:'valid'}
</div>
<div className="form-group">
<label>Password</label>
<input type="password" onChange={this.handleChange}name="pass" value={pass} className="form-control" placeholder="Enter password" />
{formErrors.passwordError?<span variant="danger">{formErrors.passwordError}</span>:'valid'}
</div>
<button onClick={this.handleSubmit}type="submit" className="btn btn-primary btn-block">Sign Up</button>
</form>
</div>
);
}
}
Sandbox linkenter link description here
In your handleValidate, you assign an error to the wrong error variable:
if(name && name.length<2)
countryError="Length of name should be more than 1 character"
It should be:
if(name && name.length<2)
nameError="Length of name should be more than 1 character"
I am trying to make a simple component in React to fill a form, but when I run it the console show me the error too much recursion
I already try to remove the event.preventDefault() on the handleSubmitbut that didn't work, I alse try to encapsulate the form using {this.props.currentUser ? (<form>...</form>) : ""
My code is the next one:
<form onSubmit={this.handleSubmit.bind(this)}>
<div className="form-group">
<label htmlFor="productName">Product name:</label>
<input
className="form-control"
type="text"
ref="productName"
required
/>
</div>
<div className="form-group">
<label htmlFor="productName">Product description:</label>
<input
className="form-control"
type="text"
ref="productDescription"
required
/>
</div>
<div className="form-group">
<label htmlFor="productName">Minimum amount increase</label>
<input
className="form-control"
type="text"
ref="minIncrease"
required
/>
</div>
<input className="btn btn-primary" type="submit" value="Submit" />
</form>
And the handleSubmit
handleSubmit(event) {
event.preventDefault();
// Find the txt field via React ref
const productName = ReactDOM.findDOMNode(this.refs.productName);
const productDescription = ReactDOM.findDOMNode(
this.refs.productDescription
);
const minIncrease = ReactDOM.findDOMNode(this.refs.minIncrease);
Meteor.call(
"auctions.insert",
productName,
productDescription,
minIncrease
);
Auctions.insert({
productName, // product name
productDescription, // product description
minIncrease, // minimum increase
value: 0, // initial value
winner: "", // actual winner
owner: Meteor.userId, // _id of logged in user
username: Meteor.user().username, // username of logged in user
createAt: new Date() // current time
});
// Clear form
ReactDOM.findDOMNode(this.refs.productName).value = "";
ReactDOM.findDOMNode(this.refs.productDescription).value = "";
ReactDOM.findDOMNode(this.refs.minIncrease).value = "";
}
I find the error, it was that in the handleSubmit it was missing .value.trim()
The correct code is:
handleSubmit(event) {
event.preventDefault();
// Find the txt field via React ref
const productName = ReactDOM.findDOMNode(
this.refs.productName
).value.trim();
const productDescription = ReactDOM.findDOMNode(
this.refs.productDescription
).value.trim();
const minIncrease = ReactDOM.findDOMNode(
this.refs.minIncrease
).value.trim();
...
}
I am trying to type on the inputs but it is not allowing me too. My state changes but it doesn't show.
I am using props to show an event OR and empty event if there is no props (no event selected).
Im sorry but Stack is telling me to add more details but I don't know what else to add, I already described my problem
class EventForm extends PureComponent {
state = {
event: this.props.selectedEvt,
query: ""
};
onFormSubmit = e => {
e.preventDefault();
this.props.addEvent(this.state.event);
};
onInputChange = evt => {
console.log(evt);
evt.persist();
let newEvt = this.state.event;
newEvt[evt.target.name] = evt.target.value;
this.setState({
event: newEvt
});
};
componentDidUpdate(prevProps) {
this.props.selectedEvt &&
this.setState({
event: this.props.selectedEvt
});
}
render() {
const { event } = this.state;
return (
<form className="card" onSubmit={this.onFormSubmit}>
<div className="form-row card-body">
<div className="form-group col-md-12">
<label hmtlfor="inputName">Event Name</label>
<input
name="title"
type="text"
className="form-control"
id="inputEventName"
onChange={this.onInputChange}
value={event.title}
/>
<label hmtlfor="inputDate">Event Date</label>
<input
name="date"
type="date"
className="form-control"
id="inputEventDate"
onChange={this.onInputChange}
value={event.date}
/>
<label hmtlfor="inputDate">Event Time</label>
<input
name="time"
type="time"
className="form-control"
id="inputEventTime"
onChange={this.onInputChange}
value={event.time}
/>
<label hmtlfor="inputAddress">Address</label>
<input
name="address"
type="text"
className="form-control"
id="autocomplete"
onChange={this.onInputChange}
value={event.address}
autoComplete="new-password"
/>
<label hmtlfor="inputHost">Hosted By</label>
<input
name="host"
type="text"
className="form-control"
id="inputHost"
onChange={this.onInputChange}
value={event.host}
/>
<label hmtlfor="inputDesc">Description</label>
<textarea
name="desc"
className="form-control"
rows="5"
id="inputDesc"
wrap="soft"
onChange={this.onInputChange}
value={event.description}
/>
<button type="submit" className="btn btn-primary mt-2">
Submit
</button>
</div>
</div>
</form>
);
}
}
export default EventForm;
Every time input value change componentDidMount run and you reset state to initial state value in componentDidUpdate.
componentDidUpdate(prevProps) {
this.props.selectedEvt &&
this.setState({
event: this.props.selectedEvt // Here is the problem
});
}
Also You mutate state when input change. And because its pureComponent it will not update.
Change onInputChange to
onInputChange = evt => {
let name = evt.target.name;
let value = evt.target.value;
let newEvent = {...this.state.event};
newEvent[name] = value
this.setState({event: newEvent});
}
Please help me to understand where I am doing what mistake? I created CustomerForm React Component, which having few form fields. These form fields will add records and in another component will show records into table format.
Every thing is working fine for CustomerForm React Component, but if I am adding onSubmit function than form fields are not loading and I am getting console error as:-
Uncaught ReferenceError: onSubmit is not defined
at new CustomerForm (index.js:32590)
<button type="submit" className="btn btn-primary" onClick={ e => this.onSubmit(e)} > Submit </button>
Also please suggest any better way to write ReactJS code using Props & State...
// Let's import react for creating component
import React from "react";
// Create CustomerForm component
class CustomerForm extends React.Component{
// create constructor function for CustomerForm component
constructor(props){
// call super, so constructor function can connect with CustomerForm component
super(props);
// Use state add object with their property and value
this.state = {
firstName : "",
lastName : "",
phoneNo : "",
issue : "",
}
// Create changeData function
// changeData = e => {
// this.setState({
// [e.target.name] : e.target.value
// });
// };
onSubmit = e => {
e.preventDefault();
console.log(this.state);
}
} // close constructor function
render(){
return(
<form>
<div className="form-group">
<label htmlFor="fname">First name</label>
<input
type="text"
className="form-control"
id="fname"
placeholder="First name"
value={this.state.firstName}
onChange={e => this.setState({ firstName: e.target.value })}
/>
{/* call setState for change firstName value
question - I created changeData function which target name attribute and change value for form fields, but it's not working
onChange={e => this.changeData(e)}
*/}
</div>
<div className="form-group">
<label htmlFor="lname">Last name</label>
<input
type="text"
className="form-control"
id="lname"
placeholder="Last name"
value={this.state.lastName}
onChange={e => this.setState({ lastName: e.target.value })}
/>
{/* call setState for change lastName value */}
</div>
<div className="form-group">
<label htmlFor="phone">Phone no.</label>
<input
type="text"
className="form-control"
id="phone"
placeholder="Phone no."
value={this.state.phoneNo}
onChange={e => this.setState({phoneNo: e.target.value})}
/>
{/* call setState for change phoneNo value */}
</div>
<div className="form-group">
<label htmlFor="issue">Issue</label>
<textarea
className="form-control"
id="issue"
rows="3"
value={this.state.issue}
onChange={e => this.setState({issue: e.target.value})}
>
{/* call setState for change issue value */}
</textarea>
</div>
<button
type="submit"
className="btn btn-primary"
onClick={ e => this.onSubmit(e)}
>
Submit
</button>
</form>
);
}
}
export default CustomerForm;
You're declaring a variable named onSubmit on the constructor and trying to access it with this.onSubmit, like a property.
You can do this in your constructor:
this.onSubmit = e => {
e.preventDefault();
console.log(this.state);
}
The suggestion
A better way to accomplish this is extracting your onSubmit method to a class method, with makes your code more readable and more consistent. Would be something like this:
// Let's import react for creating component
import React from "react";
// Create CustomerForm component
class CustomerForm extends React.Component{
// create constructor function for CustomerForm component
constructor(props){
// call super, so constructor function can connect with CustomerForm component
super(props);
// Use state add object with their property and value
this.state = {
firstName : "",
lastName : "",
phoneNo : "",
issue : "",
}
}
/////////
/// Your submit handler is now a method in the CustomerForm class,
/// so you can access with the keyword "this"
onSubmit(e) {
e.preventDefault();
console.log(this.state);
}
render(){
return(
<form onSubmit={e => this.onSubmit(e)}>
{/* Note that I've changed your handler to form,
is usually better than put on a button, since you're using a form already */}
<div className="form-group">
<label htmlFor="fname">First name</label>
<input
type="text"
className="form-control"
id="fname"
placeholder="First name"
value={this.state.firstName}
onChange={e => this.setState({ firstName: e.target.value })}
/>
{/* call setState for change firstName value
question - I created changeData function which target name attribute and change value for form fields, but it's not working
onChange={e => this.changeData(e)}
*/}
</div>
<div className="form-group">
<label htmlFor="lname">Last name</label>
<input
type="text"
className="form-control"
id="lname"
placeholder="Last name"
value={this.state.lastName}
onChange={e => this.setState({ lastName: e.target.value })}
/>
{/* call setState for change lastName value */}
</div>
<div className="form-group">
<label htmlFor="phone">Phone no.</label>
<input
type="text"
className="form-control"
id="phone"
placeholder="Phone no."
value={this.state.phoneNo}
onChange={e => this.setState({phoneNo: e.target.value})}
/>
{/* call setState for change phoneNo value */}
</div>
<div className="form-group">
<label htmlFor="issue">Issue</label>
<textarea
className="form-control"
id="issue"
rows="3"
value={this.state.issue}
onChange={e => this.setState({issue: e.target.value})}
>
{/* call setState for change issue value */}
</textarea>
</div>
<button
type="submit"
className="btn btn-primary"
>
Submit
</button>
</form>
);
}
}
export default CustomerForm;
Controlled Components
Just one more thing I think it may be helpful to you (I've noted your comment about changeData) so if you not resolve the way to do controlled inputs, this minimalist example may help you, with a onChangeHandler I usually use:
import React from 'react';
export default class MyControlledComponent extends React.Component {
constructor(props){
super(props);
// Initiating the first value for our controlled component
this.state = {
name: ""
}
}
submitHandler(e) {
e.preventDefault();
console.log('Hi, ' + this.state.name + '!');
}
onChangeHandler(e) {
const { name, value } = e.target
/*
Here we using the name property of your input to
increase reuse of this function
*/
this.setState({
[name]: value
});
}
render(){
return (
<div className="my-app">
<form onSubmit={e => this.submitHandler(e)}>
<input type="text"
name="name"
value={this.state.name}
onChange={e => this.onChangeHandler(e)} />
<button>Send!</button>
</form>
</div>
)
}
}
Hope it helps!
Your onSubmit function is not bind either bind it in constructor or use fat arrow properly like
{(return)=>{functionname()}}