ReactJs
I'm developing a simple form using ReactJS and I made a form and validations.
I made the form and handled the validations
But now I need to make it send an email with the form details using SendGrid or something similar
This is my code - App.js:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Form from './Form.js';
class App extends Component {
render() {
return (
<div className="App">
<h2>React Form - Jones Task</h2>
<Form />
</div>
);
}
}
export default App;
Form.Js
import React, { Component } from 'react';
import { FormErrors } from './FormErrors';
import './Form.css';
class Form extends Component {
constructor (props) {
super(props);
this.state = {
fname: '',
lname: '',
email: '',
phone: '',
formErrors: {fname: '', lname: '', email: '', phone: ''},
fnameValid: false,
lnameValid: false,
emailValid: false,
phoneValid: false,
formValid: false
}
this.handleSubmit = this.handleSubmit.bind(this);
}
handleUserInput = (e) => {
const name = e.target.name;
const value = e.target.value;
this.setState({[name]: value},
() => { this.validateField(name, value) });
}
validateField(fieldName, value) {
let fieldValidationErrors = this.state.formErrors;
let fnameValid = this.state.fnameValid;
let lnameValid = this.state.lnameValid;
let emailValid = this.state.emailValid;
let phoneValid = this.state.phoneValid;
switch(fieldName) {
case 'fname':
fnameValid = value.match(/^[a-zA-Z]*$/);
fieldValidationErrors.first = fnameValid ? '' : ' name is invalid. ';
fieldValidationErrors.first += value.length >= 2 ? '' : ' name should contain at least 2 chars. ';
break;
case 'lname':
lnameValid = value.match(/^[a-zA-Z]*$/) && value.length >= 2;
fieldValidationErrors.last = lnameValid ? '' : ' name is invalid. ';
fieldValidationErrors.last += value.length >= 2 ? '' : ' name should contain at least 2 chars. ';
break;
case 'email':
emailValid = value.match(/^([\w.%+-]+)#([\w-]+\.)+([\w]{2,})$/i);
fieldValidationErrors.email = emailValid ? '' : ' is invalid. ';
break;
case 'phone':
phoneValid = value.length == 10;
fieldValidationErrors.phone = phoneValid ? '' : ' should contain 10 digits exactly. ';
fieldValidationErrors.phone += value.match(/^\d+$/) ? '' : ' should contain numbers only. ';
break;
default:
break;
}
this.setState({formErrors: fieldValidationErrors,
fnameValid: fnameValid,
lnameValid: lnameValid,
emailValid: emailValid,
phoneValid: phoneValid
}, this.validateForm);
}
validateForm() {
this.setState({formValid: this.state.fnameValid && this.state.lnameValid && this.state.emailValid && this.state.phoneValid});
}
errorClass(error) {
return(error.length === 0 ? '' : 'has-error');
}
handleSubmit(event) {
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: 'avivday#gmail.com',
from: 'avivday#gmail.com',
subject: 'JonesForm',
text: 'and easy to do anywhere, even with Node.js',
html: '<strong>and easy to do anywhere, even with Node.js</strong>',
};
sgMail.send(msg);
event.preventDefault();
}
render () {
return (
<form className="demoForm" onSubmit={this.handleSubmit}>
<div className="panel panel-default">
<FormErrors formErrors={this.state.formErrors} />
</div>
<br />
<div className={"form-group ${this.errorClass(this.state.formErrors.fname)}"}>
<input type="text" className="form-control" name="fname"
placeholder="First Name"
value={this.state.fname}
onChange={this.handleUserInput} />
</div>
<br />
<div className={"form-group ${this.errorClass(this.state.formErrors.lname)}"}>
<input type="text" className="form-control" name="lname"
placeholder="Last Name"
value={this.state.lname}
onChange={this.handleUserInput} />
</div>
<br />
<div className={"form-group ${this.errorClass(this.state.formErrors.email)}"}>
<input type="email" required className="form-control" name="email"
placeholder="Email"
value={this.state.email}
onChange={this.handleUserInput} />
</div>
<br />
<div className={"form-group ${this.errorClass(this.state.formErrors.phone)}"}>
<input type="text" className="form-control" name="phone"
placeholder="Phone Number"
value={this.state.phone}
onChange={this.handleUserInput} />
</div>
<br/>
<button type="submit" disabled={!this.state.formValid}>Submit</button>
</form>
)
}
}
export default Form;
On submit, nothing happens.
I did echo the enviorment on node.js command prompt
Thank you!
Related
import React, { Component } from 'react'
class ContactForm extends Component {
state = {
name: '',
email: '',
subject: '',
lastname: '',
events: '',
notes: '',
error: {}
}
changeHandler = (e) => {
const error = this.state.error;
error[e.target.name] = ''
this.setState({
[e.target.name]: e.target.value,
error
})
}
subimtHandler = (e) => {
e.preventDefault();
const { name,
email,
subject,
lastname,
events,
notes, error } = this.state;
if (name === '') {
error.name = "Please enter your name";
}
if (email === '') {
error.email = "Please enter your email";
}
if (subject === '') {
error.subject = "Please enter your subject";
}
if (lastname === '') {
error.lastname = "Please enter your Lastname";
}
if (events === '') {
error.events = "Select your event list";
}
if (notes === '') {
error.notes = "Please enter your note";
}
if (error) {
this.setState({
error
})
}
if (error.name === '' && error.email === '' && error.email === '' && error.lastname === '' && error.subject === '' && error.events === '' && error.notes === '') {
this.setState({
name: '',
email: '',
subject: '',
events: '',
notes: '',
error: {}
})
}
}
render(){
const { name,
email,
subject,
lastname,
error } = this.state;
return(
<form onSubmit={this.subimtHandler} className="form">
<div className="row">
<div className="col-lg-6 col-md-12">
<div className="form-field">
<input value={name} onChange={this.changeHandler} type="text" name="name" placeholder="Name"/>
<p>{error.name ? error.name : ''}</p>
</div>
</div>
<div className="col-lg-6 col-md-12">
<div className="form-field">
<input value={lastname} onChange={this.changeHandler} type="text" name="lastname" placeholder="Lastname"/>
<p>{error.lastname ? error.lastname : ''}</p>
</div>
</div>
<div className="col-lg-12">
<div className="form-field">
<input onChange={this.changeHandler} value={email} type="email" name="email" placeholder="Email"/>
<p>{error.email ? error.email : ''}</p>
</div>
</div>
<div className="col-lg-12">
<div className="form-field">
<input onChange={this.changeHandler} value={subject} type="text" name="subject" placeholder="Subject"/>
<p>{error.subject ? error.subject : ''}</p>
</div>
</div>
<div className="col-lg-12">
<div className="form-field">
<textarea name="message" placeholder="Message"></textarea>
</div>
</div>
<div className="col-lg-12">
<div className="form-submit">
<button type="submit" className="theme-btn">Send Message</button>
</div>
</div>
</div>
</form>
)
}
}
export default ContactForm;
I am learning React. So, don't understand what the error is about. There are many other errors like this. all are like
export 'useState' (imported as 'useState') was not found in 'react'
(module has no exports)
export 'Fragment' (imported as 'Fragment')
was not found in 'react' (module has no exports)
export 'createElement' (imported as 'React') was not found in 'react'
(module has no exports)
export 'cloneElement' (imported as 'React')
was not found in 'react' (module has no exports)
and so on.
I have seven different input fields and updating the state with the entered value. After that, I am concatenating all the state values and updating the contractNum state but it is not being updated correctly. It is missing the first state (this.state.contact.sys) value. I am not sure how to get the right concatenated value. Any help is much appreciated.
export default class test extends Component {
state = {
contact: {
sys: '',
co: '',
lgr: '',
mgr: '',
sub: '',
serial: '',
num: ''
},
contractNum: ''
};
test = testValue => {
this.setState({
contractNum: testValue
});
};
handleChangeFor = propertyName => event => {
const { contact } = this.state;
const newContact = {
...contact,
[propertyName]: event.target.value
};
this.setState({ contact: newContact });
let newValue =
contact.sub +
contact.co +
contact.mgr +
contact.lgr +
contact.sub +
contact.serial +
contact.num;
this.test(newValue);
};
render() {
return (
<div className="wrapper">
<div className="container">
<form>
<input
type="text"
onChange={this.handleChangeFor('sys')}
value={this.state.contact.sys}
maxLength={2}
/>
<input
type="text"
onChange={this.handleChangeFor('co')}
value={this.state.contact.co}
maxLength={1}
/>
<input
type="text"
onChange={this.handleChangeFor('mgr')}
value={this.state.contact.mgr}
maxLength={1}
/>
<input
type="text"
onChange={this.handleChangeFor('lgr')}
value={this.state.contact.lgr}
maxLength={1}
/>
<input
type="text"
onChange={this.handleChangeFor('serial')}
value={this.state.contact.serial}
maxLength={6}
/>
<input
type="text"
onChange={this.handleChangeFor('num')}
value={this.state.contact.num}
maxLength={2}
/>
<input
type="text"
onChange={this.handleChangeFor('sub')}
value={this.state.contact.sub}
maxLength={1}
/>
</form>
</div>
</div>
);
}
}
You used contact.sub instead of contact.sys when setting newValue.
I have the following React Component, which holds a form with two inputs and a button.
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: null,
password: null
}
}
emailInputChanged = (e) => {
this.setState({
email: e.target.value.trim()
});
};
passwordInputChanged = (e) => {
this.setState({
password: e.target.value.trim()
});
};
loginButtonClicked = (e) => {
e.preventDefault();
if (!this.isFormValid()) {
//Perform some API request to validate data
}
};
signupButtonClicked = (e) => {
e.preventDefault();
this.props.history.push('/signup');
};
forgotPasswordButtonClicked = (e) => {
e.preventDefault();
this.props.history.push('/forgot-password');
};
isFormValid = () => {
const {email, password} = this.state;
if (email === null || password === null) {
return false;
}
return isValidEmail(email) && password.length > 0;
};
render() {
const {email, password} = this.state;
return (
<div id="login">
<h1 className="title">Login</h1>
<form action="">
<div className={(!isValidEmail(email) ? 'has-error' : '') + ' input-holder'}>
<Label htmlFor={'loginEmail'} hidden={email !== null && email.length > 0}>email</Label>
<input type="text" className="input" id="loginEmail" value={email !== null ? email : ''}
onChange={this.emailInputChanged}/>
</div>
<div className={(password !== null && password.length === 0 ? 'has-error' : '') + ' input-holder'}>
<Label htmlFor={'loginPassword'}
hidden={password !== null && password.length > 0}>password</Label>
<input type="password" className="input" id="loginPassword"
value={password !== null ? password : ''}
onChange={this.passwordInputChanged}/>
</div>
<button type="submit" className="btn btn-default" id="loginButton"
onClick={this.loginButtonClicked}>
login
</button>
</form>
<div className="utilities">
<a href={'/signup'} onClick={this.signupButtonClicked}>don't have an account?</a>
<a href={'/forgot-password'} onClick={this.forgotPasswordButtonClicked}>forgot your password?</a>
</div>
</div>
)
}
}
export function isValidEmail(email) {
const expression = /\S+#\S+/;
return expression.test(String(email).toLowerCase());
}
The values of the inputs are stored in the component's state. I have given them initial value of null and update them using setState() in the onChange event.
In the render() method I use the state to colour inputs with invalid values. Currently I am only checking the email value against a simple regex and the password to be at least 1 character in length.
The reason I have set the initial values of the state variables to null so I can check in the layout and the initial style on the input to not be marked as "has-errors". However I need to extend the check to:
this.state.email !== null && this.state.email.length === 0
in order to work, because null has no length property.
Is there a cleaner and "React-ish" way to achieve this:
- initial state of the div holding the inputs has no class has-errors
- less checks when setting the value attribute on the input (because React doesn't accept null as value of the attribute)
Edit:
If the initial value of this.state.email and this.state.password are empty strings, I get has-error applied to the div holding the inputs. In my opinion this is bad UX, because the user hasn't done anything and he is already wrong.
The hidden attribute is used by a custom component I made, which acts like "placeholder" (gets erased if something is typed in the input).
Video below showing how my form looks like when this.state.email and this.state.password are empty strings as well how my <Label> component works:
You could create a validate function which return an errors object to know which fields are in error, and use empty strings as initial values. I don't understand what you are trying to do with the hidden attribute.
Edit: add a touched property in the state, to know which field have been touched.
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
touched: {},
};
}
emailInputChanged = e => {
this.setState({
email: e.target.value.trim(),
touched: {
...this.state.touched,
email: true,
},
});
};
passwordInputChanged = e => {
this.setState({
password: e.target.value.trim(),
touched: {
...this.state.touched,
password: true,
},
});
};
loginButtonClicked = e => {
e.preventDefault();
if (!this.isFormValid()) {
//Perform some API request to validate data
}
};
isFormValid = () => {
const errors = this.validate();
return Object.keys(errors).length === 0;
};
validate = () => {
const errors = {};
const { email, password } = this.state;
if (!isValidEmail(email)) {
errors.email = true;
}
if (password.length === 0) {
errors.password = true;
}
return errors;
};
render() {
const { email, password, touched } = this.state;
const errors = this.validate();
return (
<div id="login">
<h1 className="title">Login</h1>
<form action="">
<div
className={
(errors.email && touched.email ? 'has-error' : '') +
' input-holder'
}
>
<Label htmlFor={'loginEmail'}>email</Label>
<input
type="text"
className="input"
id="loginEmail"
value={email}
onChange={this.emailInputChanged}
/>
</div>
<div
className={
(errors.password && touched.password ? 'has-error' : '') +
' input-holder'
}
>
<Label htmlFor={'loginPassword'}>password</Label>
<input
type="password"
className="input"
id="loginPassword"
value={password}
onChange={this.passwordInputChanged}
/>
</div>
<button
type="submit"
className="btn btn-default"
id="loginButton"
onClick={this.loginButtonClicked}
>
login
</button>
</form>
</div>
);
}
}
export function isValidEmail(email) {
const expression = /\S+#\S+/;
return expression.test(String(email).toLowerCase());
}
I am trying to build a contact form where an if else statement checks the validity of the email address entered, then with a nested if else checks whether the honeypot filed has a value and sends an ajaxj post request to an aws api gateway.
The ajax post runs without problem, but the outer else is always run.
Here the code:
import React from 'react'
import './style.scss'
const $ = require('jquery')
class ContactForm extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
email:'',
subject:'',
message:'',
honeypot:'',
result:'',
alertType:'',
formErrors:{
email:'',
name:'',
message:''
},
isFormValid:false,
emailValid:false,
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const target = event.target
const value = target.value
const name = target.name
this.setState({ [name]: value })
}
handleSubmit(event) {
event.preventDefault();
var URL = "https://someaddress/";
var form =this
var data = {
name: this.cleanInput(this.state.name.trim()),
email: this.cleanInput(this.state.email.trim()),
subject: this.cleanInput(this.state.subject.trim()),
message: this.cleanInput(this.state.message.trim()),
}
this.validateField('email',data.email)
data.message = "Bilgiler şunlar:\nAdı:"+data.name+"\nEmail Adresi: "+data.email+"\nKonu:"+data.subject+"\nMesaj:"+data.message
data.subject = "[Bestpet Web Sitesinden Doldurulan Form] "+data.subject;
data.email = "info#atlaspet.com.tr";
if(this.state.emailValid ===true){
if (this.state.honeypot=== ''){
$.ajax({
type: "POST",
url: URL,
dataType: "json",
contentType: "application/json",
data: JSON.stringify(data),
success: function(){
form.setState({name:'',email:'',message:'',subject:'',result:'Form başarıyla gönderildi.',alertType:'alert alert-success'})
},
error: function () {
// show an error message
form.setState({result: 'Sorunlar oluştu. Formu gönderemedik.',alertType:'alert alert-danger'});
},
});
} else {console.log("Hi there, I guess you are not human baby");}
} else { form.setState({result: 'Lütfen girmiş olduğunuz email adresini kontrol ediniz.',alertType:'alert alert-danger'})}
}
validateField(fieldName, value) {
let fieldValidationErrors = this.state.formErrors;
let emailValid = this.state.emailValid;
;
switch (fieldName) {
case 'email':
emailValid = value.match(/^([\w.%+-]+)#([\w-]+\.)+([\w]{2,})$/i);
fieldValidationErrors.email = emailValid ? true : false;
break;
default:
break;
}
this.setState({
formErrors: fieldValidationErrors,
emailValid: fieldValidationErrors.email
}, this.validateForm);
console.log(this)
}
validateForm() {
this.setState({ isFormValid: this.state.emailValid });
}
cleanInput(input){
var search = [
'<script[^>]*?>.*?</script>', // Strip out javascript
'<[/!]*?[^<>]*?>', // Strip out HTML tags
'<style[^>]*?>.*?</style>', // Strip style tags properly
'<![sS]*?--[ \t\n\r]*>', // Strip multi-line comments
]
var text = input
// console.log(search.length)
//var output = preg_replace($search, '', input);
for (let i = 0; i < search.length; i++) {
text = text.replace(new RegExp(search[i], 'gi'), '')
}
return text
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div>
<div className={"col-md-12 "+this.state.alertType}>{this.state.result!=='' && this.state.result}</div>
<input name="honeypot" type="text" style={{display:"none"}} value={this.state.honeypot} onChange={this.handleChange}/>
</div>
<div className="form-group">
<div className="col-md-6">
<label>
Name:
{this.state.formErrors.name!=='' && <div className="col-md-12 alert alert-danger">'Sizinle iletişim kurabilmemiz için bir isim girmelisiniz'</div>}
<input name="name" type="text" value={this.state.name} className ="form-control required" onChange={this.handleChange} />
</label>
</div>
<div className="col-md-6">
<label>
email
<input type="text" name="email" className="form-control required" value={this.state.email} onChange={this.handleChange}/>
</label>
</div>
</div>
<div className="form-group">
<div className="col-md-12">
<label>
Subject
<input type="text" name="subject" className="form-control required" value={this.state.subject} onChange={this.handleChange}/>
</label>
</div>
</div>
<div className="form-group">
<div className="col-md-12">
<label>
Message
<textarea name="message" rows="6" className="form-control required" value={this.state.message} onChange={this.handleChange}/>
</label>
</div>
</div>
<div className="form-group">
<div className="col-md-12">
<input type="submit" value="Submit" className="btn btn-sm btn-block btn-primary"/>
</div>
</div>
</form>
);
}
}
export default ContactForm
The section of code I have problems with is in handleSubmit function.
Thanks for help in advance and a happy new year to all.
I have moved the validity check to handleChange function and it is now working as intended.
Thanks a lot!
I am using React/Redux for my web-app and I have the Description Class where user can edit description. Props description and propertyTypes are coming from AJAX calls.
import React, { PropTypes } from 'react';
const defaultDescription = {
about_you: '',
benefits: '',
description: '',
headline: '',
no_of_guests: 0,
property_name: '',
property_type: {
id: 1,
name: 'Apartment',
},
};
class Description extends React.Component {
static propTypes = {
description: PropTypes.object,
propertyTypes: PropTypes.array,
};
constructor(props) {
super(props);
this.state = {
description: defaultDescription,
};
this.handleInputChange = this.handleInputChange.bind(this);
this.propertyTypeChanged = this.propertyTypeChanged.bind(this);
this.updateDescription = this.updateDescription.bind(this);
}
// componentWillMount() {
// if (!this.props.description) {
// this.setState({
// description: defaultDescription,
// });
// }
// }
componentWillReceiveProps(nextProps) {
if (nextProps && nextProps.description && nextProps.propertyTypes) {
const newDescription = nextProps.description.description ? merge(defaultDescription, nextProps.description) : merge(nextProps.description, defaultDescription);
this.setState({
description: newDescription,
});
}
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
description[name]: value // <---- I want to update my state here
});
}
updateDescription(event) {
event.preventDefault();
console.log(this.state);
}
render() {
return (
<form name="descriptionForm" onSubmit={this.updateDescription}>
<input name="no_of_guests" value={this.state.description.no_of_guests} onChange={this.handleInputChange} /><br />
<input name="property_name" value={this.state.description.property_name} floatingLabelText="Property Name" onChange={this.handleInputChange} /><br />
<input name="headline" value={this.state.description.headline} floatingLabelText="Headline" onChange={this.handleInputChange} /><br />
<input name="summary" value={this.state.description.summary} floatingLabelText="Summary" onChange={this.handleInputChange} /><br />
<input name="description" value={this.state.description.description} floatingLabelText="Description" onChange={this.handleInputChange} /><br />
<input name="about_you" value={this.state.description.about_you} floatingLabelText="About You" onChange={this.handleInputChange} /><br />
<input name="why" value={this.state.description.why} floatingLabelText="Why ?" onChange={this.handleInputChange} /><br />
<input name="benefits" value={this.state.description.benefits} floatingLabelText="Benefits" onChange={this.handleInputChange} /><br />
<button value="Save" type="submit" />
</form>
);
}
}
export default Description;
I want to update the form but whenever the onChange event is fired , I can not update the state, and Input field is not changed.
How do I handle this case.
Any help would be appreciated.
Thanks.
You could write the update like this:
const desc = Object.assign({}, this.state.description);
desc[name] = value;
this.setState({
description: desc
});
This is how I would update in your case
const newState = {...this.state.description};
newState[name] = value;
this.setState({
description: newState,
});
Input elements should not switch from controlled to uncontrolled (or vice versa) [[https://facebook.github.io/react/docs/forms.html#controlled-components]]