bad request on submission - javascript

i am trying to submit form . but getting error 400 bad request . using formik for form in react js
error : Bad request 400
i am trying to submit form . but getting error 400 bad request . using formik for form in react js
error : Bad request 400
i am trying to submit form . but getting error 400 bad request . using formik for form in react js
error : Bad request 400
here is my code
import React, { Component } from 'react';
import withRedux from '../../redux';
// import { Card } from "react-bootstrap";
// import { CardBody } from "reactstrap";
// import { Row, Form, Col, Button } from "react-bootstrap";
import axios from 'axios';
import { Formik, Form, Field, ErrorMessage, connect } from 'formik';
import {
Card,
CardBody,
Row,
Col,
CardSubtitle,
Button,
CardHeader,
FormGroup,
Label,
} from 'reactstrap';
const NewEdit = (props) => {
const [company, setCompany] = React.useState({ ...props.company });
const changeHandler = (e) => {
const newVal = { [e.target.name]: e.target.value };
setCompany({
...company,
...newVal,
});
};
const saveUpdateHandler = (e) => {
// e.preventDefault();
// const validate = (e) => {
// const {
// company_name,
// company_email,
// company_website,
// company_phone,
// company_address,
// } = e;
// console.log(e);
const id = props.match.params.companyId;
delete company.id;
if (id) {
axios
.put(`/company/${id}`, company)
.then((response) => {
console.log(response);
setTimeout(() => {
props.history.push('/company');
}, 2000);
})
.catch((error) => {
console.log(error);
});
} else {
axios
.post(`/company`, company)
.then((response) => {
console.log(response);
setTimeout(() => {
props.history.push('/company');
}, 2000);
})
.catch((error) => {
console.log(error);
});
}
// };
};
const validate = (e) => {
console.log('e', e);
const errors = {};
if (!e.company_name) {
errors.company_name = 'Required';
} else if (e.company_name.length > 15) {
errors.company_name = 'Must be 15 characters or less';
}
if (!e.company_address) {
errors.company_address = 'Required';
} else if (e.company_address.length > 20) {
errors.company_address = 'Must be 20 characters or less';
}
if (!e.company_phone) {
errors.company_phone = 'Required';
} else if (e.company_phone.length < 11) {
errors.company_phone = 'Must be 11 characters or more';
}
if (!e.company_email) {
errors.company_email = 'Required';
} else if (
!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(e.company_email)
) {
errors.company_email = 'Invalid email address';
}
console.log('err', errors);
return errors;
};
return (
<Card>
<CardHeader>Sign in to your account</CardHeader>
<CardBody>
<CardSubtitle></CardSubtitle>
{/* <hr /> */}
<Formik
initialValues={
props.company
? { ...props.company }
: {
company_name: '',
company_email: '',
company_website: '',
company_address: '',
company_phone: '',
}
}
validate={validate}
onSubmit={saveUpdateHandler}
>
<Form>
<FormGroup>
<Label>Company Name</Label>
<Field
type='text'
className='form-control'
value={company.company_name}
name='company_name'
/>
<ErrorMessage
name='company_name'
component='small'
className='text-danger'
/>
</FormGroup>
<FormGroup>
<Label>Company Email</Label>
<Field
type='text'
className='form-control'
value={company.company_email}
name='company_email'
/>
<ErrorMessage
name='company_email'
component='small'
className='text-danger'
/>
</FormGroup>
<FormGroup>
<Label>Company Website</Label>
<Field
type='text'
className='form-control'
value={company.company_website}
name='company_website'
/>
<ErrorMessage
name='company_website'
component='small'
className='text-danger'
/>
</FormGroup>
<FormGroup>
<Label>Company Address</Label>
<Field
type='text'
className='form-control'
value={company.company_address}
name='company_address'
/>
<ErrorMessage
name='company_address'
component='small'
className='text-danger'
/>
</FormGroup>
<FormGroup>
<Label>Company Phone</Label>
<Field
type='text'
className='form-control'
value={company.company_phone}
name='company_phone'
/>
<ErrorMessage
name='company_phone'
component='small'
className='text-danger'
/>
</FormGroup>
<Row>
<div className='col'>
<p>
<small>
<a href='/' className='text-secondary'>
Forgot password?
</a>
<br />
</small>
<a href='/'>Sign up for a new account</a>
</p>
</div>
<div className='col-auto'>
<Button variant='primary' type='submit'>
Save
</Button>
</div>
</Row>
</Form>
</Formik>
</CardBody>
</Card>
);
};
export default withRedux(NewEdit);

I solve my self first part :
by changing compnay ----> e now post method working but edit still not working ??
else {
axios
.post(`/company`, e)
.then((response) => {
console.log(response);
setTimeout(() => {
props.history.push('/company');
}, 2000);
})
.catch((error) => {
console.log(error);
});
}
// };
};

Related

I have this error while using FormBuilder jquery : Uncaught TypeError: FormBuilder.setState is not a function

I'm using FormBuilder jquery module in my react js project
this is the link :
https://formbuilder.online/docs
I want to change my state after getting the json result from the onSave function of Form Builder here is the documentation :
https://formbuilder.online/docs/formBuilder/options/onSave/
but the state is also unknown..
import $ from "jquery";
import React, { Component, createRef } from "react";
import SideBar from "../Components/dashboard/sidebar/Sidebar";
import Navbar from "../Components/dashboard/navbar/Navbar";
import Card from 'react-bootstrap/Card';
import { Form, Button } from 'react-bootstrap';
window.jQuery = $;
window.$ = $;
require("jquery-ui-sortable");
require("formBuilder");
const InitialFormBuilderData = [
];
class FormBuilder extends Component {
constructor(props) {
super(props)
this.state = {
userId: '1010',
nameForm: '',
description: '',
dateCreation: new Date(),
cmpBtn: '',
cmpCalendrier: '',
cmpInput: '',
cmpRadioCheck: '',
cmpSelectopt: '',
formCmps: [
{
"idForm": 1010
}
]
};
}
changeHandler = (e) => {
this.setState({ [e.target.name]: e.target.value })
}
submitHandler = (e) => {
const { cmpBtn, cmpCalendrier, cmpInput, cmpRadioCheck, cmpSelectopt } = FormBuilder.state;
e.preventDefault()
console.log(this.state)
//fetch('https://localhost:7049/api/Form', {
// method: 'POST',
// headers: { 'Content-type': 'application/json' },
// body: JSON.stringify(this.state)
//}).then(r => r.json()).then(res => {
// window.alert("Formulaire Ajouté avec succès . !")
//}
//);
}
//form builder component
fb = createRef();
componentDidMount() {
var fbEditor = this.fb.current;
console.log(this.fb);
let options = {
formData: InitialFormBuilderData,
disabledActionButtons: ["clear", "data"],
onSave: function (evt, formData) {
let result = formBuilder.formData;
console.log(result);
const myObj = JSON.parse(result);
console.log(myObj);
myObj.filter((val) => {
if (val.type == 'button') {
console.log(val.type);
FormBuilder.setState({
cmpBtn: val.label
})
} else {
if (val.type == 'date') {
console.log(val.type);
FormBuilder.setState({
cmpCalendrier: val.label
})
} else {
if (val.type == 'radio-group') {
console.log(val.type);
FormBuilder.setState({
cmpRadioCheck: val.label
})
} else {
if (val.type == 'select') {
console.log(val.type);
FormBuilder.setState({
cmpSelectopt: val.label
})
} else {
console.log(val.type)
FormBuilder.setState({
cmpInput: val.label
})
}
}
}
}
}
)
}
}
var formBuilder = $(fbEditor).formBuilder(options);
//$(".render-wrap").formRender({ formData });
//localStorage.setItem("formData", JSON.stringify(formData));
}
render() {
const { nameForm, userId, description } = this.state;
return (
<div className="home">
<SideBar />
<div className="homeContainer">
<Navbar />
<Card>
<Card.Header></Card.Header>
<Card.Body>
<Card.Title>Donnez un nom a votre formulaire</Card.Title>
<Form onSubmit={this.submitHandler}>
<Form.Group className="mb-3" controlId="formBasicName">
<Form.Label>Nom Formulaire : </Form.Label>
<br />
<Form.Control type="text" placeholder="form name" value={nameForm} name="nameForm" onChange={this.changeHandler} area-required />
</Form.Group>
<Form.Group className="mb-3" controlId="formBasicName">
<Form.Label>id utilisateur : </Form.Label>
<br />
<Form.Control type="text" placeholder="user id" value={1010} name="userId" onChange={this.changeHandler} area-required />
</Form.Group>
<Form.Group className="mb-3" controlId="formBasicName">
<Form.Label>Description : </Form.Label>
<br />
<Form.Control type="text" placeholder="description" value={description} name="description" onChange={this.changeHandler} area-required />
</Form.Group>
<Button variant="primary" type="submit" onClick={this.submitHandler}>
Ajouter
</Button>
</Form>
<div id="fb-editor" ref={this.fb} />
</Card.Body>
</Card>
</div>
</div>
)
};
}
export default FormBuilder;

How to integrate react-intl-tel-input-v2

While using react-intl-tel-input-v2 I was getting this error:-× TypeError: Cannot read properties of null (reading 'e') I have install the react-intl-tel-input-v2 from npm Try many things but nothing work if anyone know the solution please help Even if you know any other npm package which help me please suggest
I was getting the error in this part:-
handleChange = (e) => { const { name, value } = e.target this.setState({ [name]: value
This is my code
import React from 'react'
import firebase from './firebase'
import 'firebase/auth';
import "./App.css";
import { getDatabase, ref, child, get } from "firebase/database";
// import PhoneInput from 'react-phone-number-input'
// import $ from 'jquery';
// import intlTelInputUtils from 'jquery';
import ReactIntlTelInput from 'react-intl-tel-input-v2';
import 'intl-tel-input/build/css/intlTelInput.css';
class Login extends React.Component {
handleChange = (e) => {
const { name, value } = e.target
this.setState({
[name]: value
})
this.setState({ phoneNumber: value }, () => {
console.log(this.state.phoneNumber);
});
}
configureCaptcha = () =>{
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
'size': 'invisible',
'callback': (response) => {
// reCAPTCHA solved, allow signInWithPhoneNumber.
this.onSignInSubmit();
console.log("Recaptca varified")
},
defaultCountry: "IN"
}
);
}
onSignInSubmit = (e) => {
e.preventDefault()
this.configureCaptcha()
const phoneNumber = this.state.mobile
console.log(phoneNumber)
const appVerifier = window.recaptchaVerifier;
const dbRef = ref(getDatabase());
get(child(dbRef, `Users/${phoneNumber}`)).then((snapshot) => {
if (snapshot.exists()) {
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
.then((confirmationResult) => {
// SMS sent. Prompt user to type the code from the message, then sign the
// user in with confirmationResult.confirm(code).
window.confirmationResult = confirmationResult;
alert('An OTP has been sent to your registered mobile number')
localStorage.setItem("Phone_No", phoneNumber)
console.log(localStorage.getItem('Phone_No'));
}).catch((error) => {
console.error(error);
alert("Oops! Some error occured. Please try again.")
});
}
else {
alert('Sorry, this mobile number is not registered with us. Please use your registered mobile number.');
}
})
}
onSubmitOTP = (e) => {
e.preventDefault()
const code = this.state.otp
console.log(code)
window.confirmationResult.confirm(code).then((result) => {
// User signed in successfully.
const Users = result.user;
console.log(JSON.stringify(Users))
this.props.history.push("/home");
}).catch((error) => {
alert("You have entered wrong code")
});
}
render() {
return (
<div className="Main-header">
<img src="./55k-logo.png" alt="Company Logo" style={{ height: "80px", width: "200px" }} />
<br />
<div>
<h2>Login Form</h2>
<p>Limtless Water. From Unlimited Air.</p>
<form onSubmit={this.onSignInSubmit}>
<div id="sign-in-button"></div>
{/* <PhoneInput */}
<label>Mobile Number</label> <br />
{/* for="phoneNumber" */}
<ReactIntlTelInput
type="tel" id="phone" name="mobile" placeholder="Enter Your Number" required onChange={this.handleChange}
value={this.state.value}
// onChange={this.handleChange}
/>
{/* <input type="tel" id="phone" name="mobile" placeholder="Enter Your Number" required onChange={this.handleChange} /> */}
<div className="buttons">
<button type="submit">Submit</button>
</div>
</form>
</div>
<div>
<form onSubmit={this.onSubmitOTP}>
<label >Code</label> <br />
{/* for="code" */}
<input type="number" name="otp" placeholder="Enter The 6 Digit OTP" required onChange={this.handleChange} />
<div className="buttons" >
<button type="submit">Submit</button>
</div>
</form>
</div>
</div>
)
}
}
export default Login;

How to validate Form in React by pressing submit button

i am learning React and working on my pet-project. So, i have a form with custom input components and I want to validate them only when the user clicks on the button, but I don't know how. Full input check is whether the form is not empty and filled out correctly. I tried to create a model for subscribing components to the button click event, but when the form was filled correctly, the states were updated not after 1, but after 2 clicks, and I don't know why.
Screen of form in browser:
Code of Form component:
import React from 'react';
import { FormBox } from '../FormBox';
import styles from './checkoutpage.module.scss'
export function CheckoutPage() {
return (
<div className={styles.container}>
<div className={styles.formContainer}>
<h2>checkout</h2>
<legend>Contact info</legend>
<FormBox
type='email'
placeholder='Email address'
errorMessage='Enter a valid email address'
/>
<legend>Shipping info</legend>
<FormBox
type='name'
placeholder='Name'
/>
<FormBox
type='text'
placeholder='Streer address'
/>
<FormBox
type='text'
placeholder='Apt / Suite / Other (optional)'
/>
<div className={styles.divideBox}>
<FormBox
type='text'
placeholder='City'
/>
<FormBox
type='text'
placeholder='Province'
/>
</div>
<div className={styles.divideBox}>
<FormBox
type='text'
placeholder='Postal Code'
/>
<FormBox
type='text'
placeholder='Country'
/>
</div>
<FormBox
type='text'
placeholder='Phone Number'
/>
</div>
</div>
);
}
Code of Input component:
import React, { useEffect, useRef, useState } from "react";
import styles from "./formbox.module.scss";
interface IFormBox {
type: string;
placeholder: string;
errorMessage?: string;
}
export function FormBox({ type, placeholder, errorMessage }: IFormBox) {
const [valid, setValid] = useState(true);
const ref = useRef<HTMLInputElement>(null);
const checkValidityChange = () => {
if (ref.current) {
setValid(ref.current.checkValidity());
}
}
useEffect(() => {
if (ref.current) {
if (valid && ref.current.value !== "") {
ref.current.style.backgroundColor = "#e8f0fe";
}
else {
ref.current.style.backgroundColor = "white";
}
}
}, [valid])
return (
<div className={styles.container}>
<input
ref={ref}
onChange={checkValidityChange}
name="formInput" type={type}
placeholder={placeholder}
onBlur={() => {if (ref.current && ref.current.value === "") ref.current.style.backgroundColor = "white";}}
/>
{!valid && errorMessage && (
<span>{'* '.concat(errorMessage)}</span>
)}
</div>
);
}
Method i tried to use:
Form component:
import React, { useEffect, useRef, useState } from 'react';
import { FormBox } from '../FormBox';
import styles from './checkoutpage.module.scss'
export function CheckoutPage() {
const [formValidity, setFormValidity] = useState(false);
const [emailValidity, setEmailValidity] = useState(false);
const [nameValidity, setNameValidity] = useState(false);
const [addressValidity, setAddressValidity] = useState(false);
const [aptValidity, setAptValidity] = useState(false);
const [cityCalidity, setCityValidity] = useState(false);
const [provinceValidity, setProvinceValidity] = useState(false);
const [codeValidity, setCodeValidity] = useState(false);
const [countryValidity, setCountryValidity] = useState(false);
const [phoneValidity, setPhoneValidity] = useState(false);
const refButton = useRef<HTMLButtonElement>(null);
function subscrition (event: MouseEvent, setValidity: (v: boolean) => void, setValidityValue: boolean) {
if (event.target === refButton.current) {
setValidity(setValidityValue);
}
}
const getValidity = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault()
const result = emailValidity && nameValidity && addressValidity && aptValidity && cityCalidity && provinceValidity && codeValidity && countryValidity && phoneValidity
setFormValidity(result)
console.log(formValidity)
}
return (
<div className={styles.container}>
<form className={styles.formContainer}>
<h2>checkout</h2>
<legend>Contact info</legend>
<FormBox
subscrition={subscrition}
type='email'
placeholder='Email address'
errorMessage='Enter a valid email address'
setFullValidity={setEmailValidity}
/>
<legend>Shipping info</legend>
<FormBox
subscrition={subscrition}
type='name'
placeholder='Name'
setFullValidity={setNameValidity}
/>
<FormBox
subscrition={subscrition}
type='text'
placeholder='Streer address'
setFullValidity={setAddressValidity}
/>
<FormBox
subscrition={subscrition}
type='text'
placeholder='Apt / Suite / Other (optional)'
setFullValidity={setAptValidity}
/>
<div className={styles.divideBox}>
<FormBox
subscrition={subscrition}
type='text'
placeholder='City'
setFullValidity={setCityValidity}
/>
<FormBox
subscrition={subscrition}
type='text'
placeholder='Province'
setFullValidity={setProvinceValidity}
/>
</div>
<div className={styles.divideBox}>
<FormBox
subscrition={subscrition}
type='text'
placeholder='Postal Code'
setFullValidity={setCodeValidity}
/>
<FormBox
subscrition={subscrition}
type='text'
placeholder='Country'
setFullValidity={setCountryValidity}
/>
</div>
<FormBox
subscrition={subscrition}
type='text'
placeholder='Phone Number'
setFullValidity={setPhoneValidity}
/>
<button ref={refButton} onClick={getValidity} type="submit" className={styles.btn}>Place Your Order</button>
{formValidity && (
<span style={{backgroundColor: "green"}}>Form is valid</span>
)}
{!formValidity && (
<span style={{backgroundColor: "red"}}>Form is invalid</span>
)}
</form>
</div>
);
}
Input component:
import React, { useEffect, useRef, useState } from "react";
import styles from "./formbox.module.scss";
interface IFormBox {
type: string;
placeholder: string;
errorMessage?: string;
setFullValidity: (v: boolean) => void;
subscrition: (event: MouseEvent, setValidity: (v: boolean) => void, setValidityValue: boolean) => void;
}
export function FormBox({ type, placeholder, errorMessage, setFullValidity, subscrition }: IFormBox) {
const [valid, setValid] = useState(true);
const ref = useRef<HTMLInputElement>(null);
const checkValidityChange = () => {
if (ref.current) {
setValid(ref.current.checkValidity());
}
}
useEffect(() => {
if (ref.current) {
// #ts-ignore
document.addEventListener('click', (event: MouseEvent) => {subscrition(event, setFullValidity, valid && (ref.current.value !== ""))})
}
})
useEffect(() => {
if (ref.current) {
if (valid && ref.current.value !== "") {
ref.current.style.backgroundColor = "#e8f0fe";
} else {
ref.current.style.backgroundColor = "white";
}
}
}, [valid])
return (
<div className={styles.container}>
<input
ref={ref}
onChange={checkValidityChange}
name="formInput" type={type}
placeholder={placeholder}
onBlur={() => {
if (ref.current && ref.current.value === "") {
ref.current.style.backgroundColor = "white";
}
}}
/>
{!valid && errorMessage && (
<span>{'* '.concat(errorMessage)}</span>
)}
</div>
);
}
Any advice on how to improve the code will be very warmly welcomed. Thanks for reading, and sorry if i waste your time.
You can follow this example to validate input fields. Here input reference is used like ref to validate data.
WORKING DEMO
import React from "react";
import ReactDOM from "react-dom";
import "./style.css";
function validate(name, email, password) {
// we are going to store errors for all fields
// in a signle array
const errors = [];
if (name.length === 0) {
errors.push("Name can't be empty");
}
if (email.length < 5) {
errors.push("Email should be at least 5 charcters long");
}
if (email.split("").filter(x => x === "#").length !== 1) {
errors.push("Email should contain a #");
}
if (email.indexOf(".") === -1) {
errors.push("Email should contain at least one dot");
}
if (password.length < 6) {
errors.push("Password should be at least 6 characters long");
}
return errors;
}
class SignUpForm extends React.Component {
constructor() {
super();
this.state = {
errors: []
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
const name = ReactDOM.findDOMNode(this._nameInput).value;
const email = ReactDOM.findDOMNode(this._emailInput).value;
const password = ReactDOM.findDOMNode(this._passwordInput).value;
const errors = validate(name, email, password);
if (errors.length > 0) {
this.setState({ errors });
return;
}
// submit the data...
}
render() {
const { errors } = this.state;
return (
<form onSubmit={this.handleSubmit}>
{errors.map(error => (
<p key={error}>Error: {error}</p>
))}
<input
ref={nameInput => (this._nameInput = nameInput)}
type="text"
placeholder="Name"
/>
<input
ref={emailInput => (this._emailInput = emailInput)}
type="text"
placeholder="Email"
/>
<input
ref={passwordInput => (this._passwordInput = passwordInput)}
type="password"
placeholder="Password"
/>
<button type="submit">Submit</button>
</form>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<SignUpForm />, rootElement);
More Details

React: How to change form validation from onSubmit validation for all fields, to onBlur instant validation for each field in the form?

I have the form which is functioning great, but I need to make the validation instant (as soon as the user defocuses the field, I want the error message shown for that field only). At the moment I have a validation that shows error messages when the submit button is clicked:
Here is the code:
The useForm hook
import { useState, useEffect } from 'react';
const useForm = (callback, validate, post) => {
const [values, setValues] = useState(post || {});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
useEffect(() => {
if (Object.keys(errors).length === 0 && isSubmitting) {
callback();
}
}, [errors]);
const handleSubmit = (event) => {
if (event) event.preventDefault();
console.log('values in useForm', values)
setErrors(validate(values))
setIsSubmitting(true);
};
const handleInputChange = (event) => {
event.persist();
setValues(values => ({ ...values, [event.target.name]: event.target.value }));
};
return {
handleInputChange,
handleSubmit,
values,
errors,
}
};
export default useForm;
The validate function
const validate = (values) => {
const errors = {};
if (!values.title) {
errors.title = 'Title is required'
} else if (values.title.length < 5) {
errors.title = 'Title must be at least 5 characters long'
}
if (!values.body) {
errors.body = "Blog body is required"
} else if (values.body.length < 2 || values.body.length > 20) {
errors.body = "Text has to be between 2 and 20 characters long"
}
if (!values.author) {
errors.author = "The author's name is required"
}
if (!values.number) {
errors.number = "A number is required"
}
if (!values.email) {
errors.email = 'Email is required';
} else if (
!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
) {
errors.email = 'Invalid email address';
}
return errors;
}
export default validate;
The form component
import { useHistory } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import useForm from '../hooks/useForm';
import { addPost } from '../actions';
import validate from '../helpers/validate';
const CreateForm = () => {
const dispatch = useDispatch();
const history = useHistory();
const { values, handleInputChange, handleSubmit, errors } = useForm(submit, validate)
const { title, body, author, number, email } = values;
function submit() {
console.log("No errors", values)
const post = values;
console.log('Submit form', post)
dispatch(addPost(post))
history.push('/');
}
return (
<div className="create">
<h2>Add a new blog</h2>
<form onSubmit={handleSubmit} noValidate>
<label>Blog title:</label>
<input
type="text"
required
name="title"
value={title || ""}
onChange={handleInputChange}
className={errors.title ? 'red-border' : ""}
/>
{errors.title && (<p className="danger">{errors.title}</p>)}
<label>Blog body:</label>
<textarea
required
name="body"
value={body || ""}
onChange={handleInputChange}
className={errors.body ? 'red-border' : ""}
/>
{errors.body && (
<p className="danger">{errors.body}</p>
)}
<label>Author:</label>
<input
type="text"
required
name="author"
value={author || ""}
onChange={handleInputChange}
className={errors.author ? 'red-border' : ""}
/>
{errors.author && (
<p className="danger">{errors.author}</p>
)}
<label>Number:</label>
<input
type="number"
required
name="number"
value={number || ""}
onChange={handleInputChange}
className={errors.number ? 'red-border' : ""}
/>
{errors.number && (
<p className="danger">{errors.number}</p>
)}
<label>Email:</label>
<input
type="text"
required
name="email"
value={email || ""}
onChange={handleInputChange}
className={errors.email ? 'red-border' : ""}
/>
{errors.email && (
<p className="danger">{errors.email}</p>
)}
<button>Save</button>
</form>
</div>
);
}
export default CreateForm;
As dicussed in the comments, you just need to call your setError when you want to update your error helpers. Here's a live example that flags an error if you type "error" in any of the fields: https://codesandbox.io/s/wispy-monad-3t8us?file=/src/App.js
const validateInputs = (e) => {
console.log("validating inputs");
if (e.target.value === "error")
setErrors({ ...errors, [e.target.name]: true });
else setErrors({ ...errors, [e.target.name]: false });
};
<input
type="text"
required
name="title"
value={values.title}
onChange={handleInputChange}
style={errors.title ? { border: "2px solid red" } : null}
onBlur={validateInputs}
/>
As you said in your question, you should call validate() in onBlur instead of in onSubmit.
So just add onBlur event in each of your input:
<input
type="number"
required
name="number"
onBlur={validate}
onChange={handleInputChange}
/>

React Redux-dynamically creating Redux Forms

I'm building a component which will allow users to invite their friends.
The spec for the component is that it will have several input forms for their friends' emails and companies, a button which will add more input forms, and a button which submits all the forms remotely. When forms are submitted, a spinner appears in each form until a response is received from the server, at which point, if the submit was successful, the form disappears, and if there was an error, the error is displayed.
I'm stuck on the following: in order to submit a form remotely with Redux Form, you need to pass its name to the submitting component. I want to create the forms programatically. The names will be auto-incremented integers, created by the form management component, and passed to the child forms as props. However, when I try to export the form, referencing the name as this.props.name, I get an error: 'Uncaught TypeError: Cannot read property 'props' of undefined'-"this" is undefined.
Questions:
Is my approach to solving this problem valid, or should I do something differently on a basic level?
How do I get around this? I assume it's a scoping error?
My components:
The management component (creates and deletes forms, submits them, etc.):
import React, { Component } from 'react';
import { connect } from 'react-redux'
import { submit } from 'redux-form'
import * as actions from '../../actions';
import InviteForm from './inviteForm';
class InvitationFormManager extends Component {
const buildForms = (length) =>{
for (let i = 0; i< length; i++)
{
this.setState({forms:[...this.state.forms, <InviteForm key={i} name={i}>]};
}
}
const addForm = () =>{
this.setState({forms:[...this.state.forms, <InviteForm key={(this.state.forms.length + 1)} name={(this.state.forms.length + 1)}>]});
}
const formSubmit = (form) =>
{
dispatch(submit(form.name))
.then(this.setState({
forms: this.state.forms.filter(f => f.name !== form.name)
}))
}
const submitForms = (){
for(let form of this.state.forms){formSubmit(form)}
}
constructor(props) {
super(props);
this.state = {forms:[]};
}
componentWillMount(){
buildForms(3)
}
render() {
return (<div>
<h5 className="display-6 text-center">Invite your team</h5>
{this.state.forms}
<br />
<button
type="button"
className="btn btn-primary"
onClick={submitForms}
>
Invite
</button>
<button
type="button"
className="btn btn-primary"
onClick={addForm}
>
+
</button>
</div>
);
}
}
export default connect(actions)(InvitationFormManager)
The form component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import * as actions from '../../actions';
import { Link } from 'react-router';
const renderField = ({
input,
label,
type,
meta: { touched, error, warning }
}) => (
<fieldset className="form-group">
<label htmlFor={input.name}>{label}</label>
<input className="form-control" {...input} type={type} />
{touched && error && <span className="text-danger">{error}</span>}
</fieldset>
);
class InviteForm extends Component {
constructor(props) {
super(props);
this.name = this.name.bind(this);
}
handleFormSubmit(props) {
this.props.sendInvitation(props);
}
render() {
if (this.props.submitting) {
return (
<div className="dashboard loading">
<Spinner name="chasing-dots" />
</div>
);
}
const { formName, handleSubmit } = this.props;
return (
<div className="form-container text-center">
<form
className="form-inline"
onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<div className="form-group">
<Field
name="email"
component={renderField}
type="email"
label="Email"
/>
<Field
name="company"
component={renderField}
type="text"
label="Company"
/>
</div>
</form>
<div>
{this.props.errorMessage &&
this.props.errorMessage.invited && (
<div className="error-container">
Oops! {this.props.errorMessage.invited}
</div>
)}
</div>
</div>
);
}
}
function validate(values) {
let errors = {};
if (values.password !== values.password_confirmation) {
errors.password = "Password and password confirmation don't match!";
}
return errors;
}
function mapStateToProps(state) {
return {
errorMessage: state.invite.error,
submitting: state.invite.submitting
};
}
InviteForm = reduxForm({
form: this.props.name,
validate
})(InviteForm);
export default connect(mapStateToProps, actions)(InviteForm);
The answer is, RTFM. Redux Form has this functionality as FieldArrays.
The example for Redux Form 7.0.4 is here: https://redux-form.com/7.0.4/examples/fieldarrays/
In case they move it later, here it is:
FieldArraysForm.js
import React from 'react'
import { Field, FieldArray, reduxForm } from 'redux-form'
import validate from './validate'
const renderField = ({ input, label, type, meta: { touched, error } }) =>
<div>
<label>
{label}
</label>
<div>
<input {...input} type={type} placeholder={label} />
{touched &&
error &&
<span>
{error}
</span>}
</div>
</div>
const renderHobbies = ({ fields, meta: { error } }) =>
<ul>
<li>
<button type="button" onClick={() => fields.push()}>
Add Hobby
</button>
</li>
{fields.map((hobby, index) =>
<li key={index}>
<button
type="button"
title="Remove Hobby"
onClick={() => fields.remove(index)}
/>
<Field
name={hobby}
type="text"
component={renderField}
label={`Hobby #${index + 1}`}
/>
</li>
)}
{error &&
<li className="error">
{error}
</li>}
</ul>
const renderMembers = ({ fields, meta: { error, submitFailed } }) =>
<ul>
<li>
<button type="button" onClick={() => fields.push({})}>
Add Member
</button>
{submitFailed &&
error &&
<span>
{error}
</span>}
</li>
{fields.map((member, index) =>
<li key={index}>
<button
type="button"
title="Remove Member"
onClick={() => fields.remove(index)}
/>
<h4>
Member #{index + 1}
</h4>
<Field
name={`${member}.firstName`}
type="text"
component={renderField}
label="First Name"
/>
<Field
name={`${member}.lastName`}
type="text"
component={renderField}
label="Last Name"
/>
<FieldArray name={`${member}.hobbies`} component={renderHobbies} />
</li>
)}
</ul>
const FieldArraysForm = props => {
const { handleSubmit, pristine, reset, submitting } = props
return (
<form onSubmit={handleSubmit}>
<Field
name="clubName"
type="text"
component={renderField}
label="Club Name"
/>
<FieldArray name="members" component={renderMembers} />
<div>
<button type="submit" disabled={submitting}>
Submit
</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>
Clear Values
</button>
</div>
</form>
)
}
export default reduxForm({
form: 'fieldArrays', // a unique identifier for this form
validate
})(FieldArraysForm)
validate.js
const validate = values => {
const errors = {}
if (!values.clubName) {
errors.clubName = 'Required'
}
if (!values.members || !values.members.length) {
errors.members = { _error: 'At least one member must be entered' }
} else {
const membersArrayErrors = []
values.members.forEach((member, memberIndex) => {
const memberErrors = {}
if (!member || !member.firstName) {
memberErrors.firstName = 'Required'
membersArrayErrors[memberIndex] = memberErrors
}
if (!member || !member.lastName) {
memberErrors.lastName = 'Required'
membersArrayErrors[memberIndex] = memberErrors
}
if (member && member.hobbies && member.hobbies.length) {
const hobbyArrayErrors = []
member.hobbies.forEach((hobby, hobbyIndex) => {
if (!hobby || !hobby.length) {
hobbyArrayErrors[hobbyIndex] = 'Required'
}
})
if (hobbyArrayErrors.length) {
memberErrors.hobbies = hobbyArrayErrors
membersArrayErrors[memberIndex] = memberErrors
}
if (member.hobbies.length > 5) {
if (!memberErrors.hobbies) {
memberErrors.hobbies = []
}
memberErrors.hobbies._error = 'No more than five hobbies allowed'
membersArrayErrors[memberIndex] = memberErrors
}
}
})
if (membersArrayErrors.length) {
errors.members = membersArrayErrors
}
}
return errors
}
export default validate

Categories

Resources