In my React app, I have a component call <ComingSoonForm/> Inside that, I have a TextInputField. If the Email valid, the Notify-Button is able. If the Email is invalid, the Notify-Button is disabled.
Here is my Component File:
import React, { Component } from "react";
import { TextInputField, toaster, Button, } from "evergreen-ui";
import Box from 'ui-box';
import { validateEmail } from "../FormValidation/FormValidator";
class ComingSoonForm extends Component {
constructor(props) {
super(props);
this.state = {
emailErr: {
status: true,
value: ""
},
email: "",
isDisabled: true,
};
this.handleSubmit = this.handleSubmit.bind(this);
this.checkFormStatus = this.checkFormStatus.bind(this);
}
handleEmailInput = e => {
const email = e.target.value;
this.setState({ email: email});
console.log(this.state.email);
this.checkFormStatus();
};
handleSubmit() {
if (this.checkFormStatus()) {
alert("Form is OK")
}
}
checkFormStatus() {
// form validation middleware
const { email } = this.state;
const emailErr = validateEmail(email);
if (!emailErr.status) {
this.setState({isDisabled:false})
return true;
} else {
this.setState({
emailErr,
});
return false;
}
}
render() {
return (
<div>
<Box className="welcomePageWelcomeInnerLoginButton">
<TextInputField
marginTop={15}
width={200}
onChange={this.handleEmailInput}
value={this.state.email}
type="email"
placeholder="Your email-address"
inputHeight={40}
/>
</Box>
<Button height="40" appearance="primary" marginBottom={5} className="welcomePageWelcomeInnerLoginButtonWidth" disabled={this.state.isDisabled} onClick={this.handleSubmit}>Notify Me</Button>
</div>
);
}
}
export default ComingSoonForm;
But this case doesn't work correctly. So when the command console.log(this.state.email) in the handleEmailInput Function run, I get the following data in the console:
I type one letter (t) and I get:
//ComingSoonForm.js:25
I type a second letter (t) and I get:
t //ComingSoonForm.js:25
t //FormValidator.js:10
Why do I have to enter two letters in order for one to appear in the console?
setState is asynchronous, you can pass a callback method as a second parameter like this:
handleEmailInput = e => {
const email = e.target.value;
this.setState({ email: email }, () => console.log(this.state.email));
this.checkFormStatus();
};
Related
Can someone explain why this validation is not working properly? When I click the submit button I should check to see if the field is empty, if it is it should display the error and prevent the next page. If the form has been filled in, then no message should display and clicking the button will take you to a new screen. Currently, the error displays whether or not the field is filled in and the page does not advance in either case.
import React, { Component } from 'react'
const intialState = {
account: "",
accountError: ""
};
export class LandingPage extends Component {
state = intialState;
handleChange = event => {
this.setState({
})
}
continue = e => {
e.preventDefault();
this.props.nextStep();
}
validate = () => {
let accountError= "";
if (!this.state.name) {
accountError = "You must enter an account number!";
}
if (accountError) {
this.setState({ accountError });
return false;
}
return true;
};
handleSubmit = event => {
event.preventDefault();
const isValid = this.validate();
if (isValid) {
console.log(this.state);
this.setState(intialState);
}
};
previous = e => {
e.preventDefault();
this.props.previousStep();
}
render() {
const { values } = this.props;
const { errors } = this.props;
return (
<div>
<h1>Enter an account number</h1>
<input
type="text"
required="required"
placeholder="Enter Account Number"
autoComplete='off'>
</input>
<div className="footer">
<button onClick={this.validate}>Submit</button>
</div>
<div>
{this.state.accountError}
</div>
</div>
)
}
}
export default LandingPage;
The issue is that the name in the state was not getting any value. Fix it like below.
Change the initialState to have the name field
const intialState = {
...
...
name: ""
};
handleChange needs to set the value from the event.
handleChange = (event) => {
this.setState({ [event.target.name]: event.target.value });
};
Update the validate function to handle the case when input is typed.
validate = () => {
let accountError = "";
if (!this.state.name) {
accountError = "You must enter an account number!";
}
if (accountError) {
this.setState({ accountError });
return false;
}
this.setState({ accountError: "" });
return true;
};
Add the handleChange as the onChange to the input element.
<input
...
...
...
name="name"
onChange={this.handleChange}
></input>
Working Example
I'm trying to update a database with a user's text input, and it isn't working, even after trying a bunch of different approaches.
The text input is controlled by the following component:
import React from 'react'
class Dogue extends React.Component {
constructor(props){
super(props)
this.state = {
id: '',
nameInput:''
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(e) {
this.setState({
id: Date.now(),
nameInput: e.target.value
})
}
handleSubmit(e){
e.preventDefault()
this.props.inputFunction(this.state.nameInput, this.state.id)
}
render(){
console.log(this.props.id)
return (
<div className = 'dogue-container'>
<img className = 'img' src = {this.props.dogList}/>
<br/>
<form onSubmit = {this.handleSubmit} className = 'form'>
<input
onChange ={this.handleChange}
className ='input'
type = 'text'
placeholder = 'Enter dog name'
/>
<br/>
<button className = 'button'>Submit</button>
</form>
<h2 className = 'text'>Name: {this.props.name} </h2>
</div>
)
}
}
export default Dogue
and the state update and post is controlled by the App component:
import React, { Component } from "react";
import './styles.css'
import DogList from "./DogList";
import axios from "axios";
class App extends React.Component {
constructor() {
super();
this.state = {
loading: false,
dog: [],
dogName: [],
newName:''
};
this.updateStateWithInput = this.updateStateWithInput.bind(this)
}
setData = async () => {
const x = await fetch("https://dog.ceo/api/breed/hound/images");
const y = await x.json();
const z = await y.message;
let newArr = [];
for (let i = 0; i < z.length; i++) {
if (i <= 9) {
newArr.push(z[i]);
}
}
return newArr;
};
async componentDidMount() {
this.setState({
loading: true
});
let dogPromise = await this.setData();
let dogNamePromise = await axios.get('http://localhost:3000/dogs');
this.setState({
loading: false,
dog: dogPromise,
dogName: dogNamePromise.data
});
}
//Here is the function to update state and make axios post
async updateStateWithInput (nameInput,id) {
let newDog={id:id, dogName:nameInput}
this.setState({
dogName: this.state.dogName.push(newDog)
})
await axios.post('http://localhost:3000/dogs', this.state.dogName)
.then(res => {
console.log(res)
})
}
render() {
return this.state.loading ? (
<h1 className = 'text'> Dogues Loading.....</h1>
) : (
<div>
<h1 className = 'text'>Rate My Dogue</h1>
<DogList
dogs={this.state.dog}
name={this.state.dogName}
inputFunction = {this.updateStateWithInput}
/>
</div>
);
}
}
export default App
Basically, all I'm trying to do is update an array of objects, with a new object - example as follows:
//existing array:
[
{
id: 1,
dogName: 'bruce',
},
{
id: 2,
dogName: 'borker',
},
{
id: 3,
dogName: 'henry',
},
];
//new object to be pushed into array:
{id: id of some sort, dogName: the text input from the user}
Either you use await or use then, cannot use both:
const res = await axios.post('http://localhost:3000/dogs', this.state.dogName);
console.log(res)
So, some context: Users submit a dog name via a text input, and this is controlled by the 'Dogue.jsx' component:
import React from 'react';
class Dogue extends React.Component {
constructor(props) {
super(props);
this.state = {
id: props.id,
nameInput: '',
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
nameInput: e.target.value,
});
}
handleSubmit(e) {
e.preventDefault();
this.props.inputFunction(this.state.nameInput);
}
render() {
console.log(this.props.id);
return (
<div className="dogue-container">
<img className="img" src={this.props.dogList} />
<br />
<form onSubmit={this.handleSubmit} className="form">
<input
onChange={this.handleChange}
className="input"
type="text"
placeholder="Enter dog name"
/>
<br />
<button className="button">Submit</button>
</form>
<h2 className="text">Name: {this.props.name} </h2>
</div>
);
}
}
export default Dogue;
The submitted information is then passed to 'App.jsx', where it is used to update state:
import React, {Component} from 'react';
import './styles.css';
import DogList from './DogList';
import axios from 'axios';
class App extends React.Component {
constructor() {
super();
this.state = {
loading: false,
dog: [],
dogName: [],
};
this.updateStateWithInput = this.updateStateWithInput.bind(this);
}
setData = async () => {
const x = await fetch('https://dog.ceo/api/breed/hound/images');
const y = await x.json();
const z = await y.message;
let newArr = [];
for (let i = 0; i < z.length; i++) {
if (i <= 9) {
newArr.push(z[i]);
}
}
return newArr;
};
async componentDidMount() {
this.setState({
loading: true,
});
let dogPromise = await this.setData();
let dogNamePromise = await axios.get('http://localhost:3000/dogs');
this.setState({
loading: false,
dog: dogPromise,
dogName: dogNamePromise.data,
});
}
updateStateWithInput(nameInput) {
//Here is where state is updated.
//change state, then use axios.post to submit data
}
render() {
return this.state.loading ? (
<h1 className="text"> Dogues Loading.....</h1>
) : (
<div>
<h1 className="text">Rate My Dogue</h1>
<DogList
dogs={this.state.dog}
name={this.state.dogName}
inputFunction={this.updateStateWithInput}
/>
</div>
);
}
}
export default App;
The updated state, I imagine, will be used in the axios post request to submit data to the database. So, I've got input data being sent from Dogue to App, I'm just not sure what to do now? The information currently in state looks as follows:
[
{
id: 1,
dogName: 'bruce',
},
{
id: 2,
dogName: 'borker',
},
{
id: 3,
dogName: 'henry',
},
];
I should also show my map function, in DogList.jsx:
import React from 'react';
import Dogue from './Dogue';
const DogList = (props) => {
return (
<div className="img-container">
{props.dogs.map((doggie, index) => {
return (
<Dogue
id={props.name[index] && props.name[index].id}
key={index}
dogList={doggie}
name={props.name[index] && props.name[index].dogName}
inputFunction={props.inputFunction}
/>
);
})}
</div>
);
};
export default DogList;
You can send a POST request with axios by calling:
axios.post(url, data, options);
It’s similar to the way you called the get method to make a GET request.
I’m leaving this axios cheat sheet here since it’s really useful until you get the hang of it:
https://kapeli.com/cheat_sheets/Axios.docset/Contents/Resources/Documents/index
Here are my two components. I just need to update my state in the login component. I am not sure what I am doing wrong here. I am trying to pass the data on change to the login component. The data is getting captured in e.target.value for each character, but then it resets the state.
I have tried to move the userObj inside the state as well,but does not work
import React, { Component } from 'react';
import FormHeader from './FormHeader'
class NonLoggenInForm extends Component {
render() {
return (
<div className="marginTop1 formPanel">
<FormHeader label={this.props.label}/>
{this.props.content.map((key)=>{
return <input type={key.type}
value = {key.value}
placeholder = {key.name}
required = {key.required}
onChange = {e=>this.props.onChange(e)}
className = "formInput"
name = {key.name}
key = {key.id}
/>;
})}
<button onClick={this.props.onSubmit}> Sign in</button>
</div>
);
}
}
export default NonLoggenInForm;
import React, { Component } from 'react';
import Logo from '../shared/Logo';
import NonLoggenInForm from '../shared/NonLoggenInForm';
class Login extends Component {
changeHandler = (e) => {
console.log(e.target.value);
this.setState({
[e.target.name] : e.target.value
});
}
loginHandler = (e) => {
e.preventDefault();
console.log(this.state);
}
render() {
let userObj = [
{
name : 'userId',
type: 'text',
required: true,
value : '',
id : 1
},
{
name : 'password',
type : 'password',
required : true,
value : '',
id : 2
}
];
return (
<div className="nonLoggedInPages">
<Logo/>
<NonLoggenInForm content={userObj} label="Sign in" onSubmit={this.loginHandler} onChange={this.changeHandler}/>
</div>
);
}
}
export default Login;
Moved the user Obj to state again and changed the onChange function as below
changeHandler = (e) => {
let objIndex = this.state.userObj.findIndex((ele)=>{
return ele.name === e.target.name;
});
let upadtedObject = [...this.state.userObj];
upadtedObject[objIndex].value = e.target.value;
this.setState({
userObj : upadtedObject
});
e.target.value = this.state.userObj[objIndex].value;
}
Hi i'm having problems with Flow and React.
I'm getting these errors and I want to get rid of them. What am I missing I have search everywhere. I understand that the props are missing but I can't seem to find where to define them.
Flow: property className. Property not found in props of React element form
Flow: property onSubmit. Property not found in props of React element form
export default class LoginForm extends React.Component {
_submitForm: Function;
constructor(props?: {}) {
super(props);
this.state = {
form: {
email: '',
password: '',
},
errors: {
_form: "",
email: "",
password: ""
}
};
this._submitForm = this._submitForm.bind(this);
}
_handleValues(param: string, value?: string) {
let obj = this.state;
obj['form'][param] = value;
this.setState(obj);
}
_submitForm(event: Event) {
this._clearErrors(event);
let form = this.state.form;
AxiosQueue
.post({
url: LINK.AUTHENTICATE,
data: form
})
.then(({data}) => {
if (!data.success) {
return;
}
})
.catch((response) => {
console.error(response);
});
}
render() {
const {errors, form} = this.state;
const user = UserStore.getUser();
const formText = FORM_TEXT[user.language || "en_AU"];
return (
<form className="form-inline" onSubmit={this._submitForm}>
{errors._form}
<InputEmail id="email" error={errors.email} value={form.email} callback={this._handleValues}/>
<InputPassword id="password" error={errors.password} value={form.password}
callback={this._handleValues}/>
<button type="submit" className="btn btn-default">{formText.LOGIN}</button>
</form>
);
}
}
There is conflict with your variable name form in Syntex const {errors, form} = this.state; and form component. Solution is to give some other name in this.state. Like
this.state = {
formValidation:{
//Validation properties
}
}
And consume so that will remove conflict
const {formValidation} = this.state