So i'm using this example to try and build a simple form that validates synchronously.
This is my form:
const DatasetForm: React.StatelessComponent = (props: any) => {
const { handleSubmit, pristine, reset, submitting } = props;
console.log(props);
return (<form onSubmit= { handleSubmit }>
<div>
<div>
<Field
name="firstName"
component= {renderField}
type="text"
label="First Name"
/>
</div>
<div>
<button type="submit" disabled={submitting}>Submit</button>
</div>
</div>
</form>)
}
I'm using exactly the same renderField function and this is my validate function:
const validate = (values: IValues) => {
let errors: IValues = {
firstName: ''
};
if (!values.firstName) {
errors.firstName = 'Required';
}
else if (values.firstName !== 'aaaaa') {
errors.firstName = 'Must be aaaaa';
}
return errors;
}
The handleSubmit function is a simple console.log and is passed as a prop.
Now in the example the handleSubmit function doesn't seem to be called if a field is not valid. But in my code, it is definitely called every time i click the submit button. I've stared at both my and the example code for a long time without finding what may cause this difference. Any help is appreciated.
EDIT: Adding the export function:
export default reduxForm({
form: 'simpleForm',
validate
})(DatasetForm);
You need to pass in your custom validate function to your wrapped custom form DatasetForm. Like this:
const validate2 = (values) => {
let errors = {};
if (!values.firstName) {
errors.firstName = 'Required';
}
else if (values.firstName !== 'aaaaa') {
errors.firstName = 'Must be aaaaa';
}
console.log(errors);
return errors;
}
const DatasetForm = (props) => {
const { handleSubmit, pristine, reset, submitting } = props;
return (<form onSubmit={handleSubmit}>
<div>
<div>
<Field
name="firstName"
component={renderField}
type="text"
label="First Name"
/>
</div>
<div>
<button type="submit" disabled={submitting}>Submit</button>
</div>
</div>
</form>)
}
export default reduxForm({
form: 'syncValidation',
validate: validate2,
warn
})(DatasetForm)
Check out this working sample.
Related
I am using a React library called Kendo React in order to create a simple form. What I want is to be able to submit my form when the data is changed by clicking on the Load new user button, however the current behavior is that when I click the button and populate with data the submit button does not submit form until I manually change the value of the field, why and how can I just submit the form immediately after I update the data and the button is enabled? Here is my code:
import React from 'react';
import ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '#progress/kendo-react-form';
import { Error } from '#progress/kendo-react-labels';
import { Input } from '#progress/kendo-react-inputs';
const emailRegex = new RegExp(/\S+#\S+\.\S+/);
const emailValidator = value => emailRegex.test(value) ? "" : "Please enter a valid email.";
const EmailInput = fieldRenderProps => {
const {
validationMessage,
visited,
...others
} = fieldRenderProps;
return <div>
<Input {...others} />
{visited && validationMessage && <Error>{validationMessage}</Error>}
</div>;
};
const App = () => {
const handleSubmit = dataItem => alert(JSON.stringify(dataItem, null, 2));
const [user, setUser] = React.useState({
firstName: 'John',
lastName: 'Smith',
email: 'John.Smith#email.com'
});
const loadNewUser = () => {
setUser({
firstName: 'NewFirstName',
lastName: 'NewLastName',
email: 'NewEmails#email.com'
});
};
return <React.Fragment>
<button className='k-button' onClick={loadNewUser}> Load new user </button>
<hr className='k-hr' />
<Form onSubmit={handleSubmit} initialValues={user} key={JSON.stringify(user)} render={formRenderProps => <FormElement style={{
maxWidth: 650
}}>
<fieldset className={'k-form-fieldset'}>
<legend className={'k-form-legend'}>Please fill in the fields:</legend>
<div className="mb-3">
<Field name={'firstName'} component={Input} label={'First name'} />
</div>
<div className="mb-3">
<Field name={'lastName'} component={Input} label={'Last name'} />
</div>
<div className="mb-3">
<Field name={"email"} type={"email"} component={EmailInput} label={"Email"} validator={emailValidator} />
</div>
</fieldset>
<div className="k-form-buttons">
<button type={'submit'} className="k-button" disabled={!formRenderProps.allowSubmit}>
Submit
</button>
</div>
</FormElement>} />
</React.Fragment>;
};
ReactDOM.render(<App />, document.querySelector('my-app'));
this is normal because when you use setUser a render event is triggered and not a change event on your inputs, so to submit your form on every loadNewUser click, you can add the submit logic to loadNewUser method after setUser. The last option is better as you avoid triggering the submit logic before setUser call inside loadNewUser.
Or use UseEffect to perform the submit on each change to the user object.
useEffect(() => {
console.log(user);
}, [user]);
Here is a simple example ( without Kendo ) :
import { useEffect, useState } from "react";
export default function App() {
const [user, setUser] = useState(null);
const loadNewUser = () => {
setUser({ name: "alan" });
};
const onchange = (event) => {
setUser({
...user,
name: event.target.value
});
};
const submit = () => {
console.log("submit", user);
};
useEffect(() => {
user && submit();
}, [user]);
return (
<div className="App">
<button onClick={loadNewUser}>Load new user</button>
<input
type="text"
onChange={onchange}
value={user != null ? user.name : ""}
/>
<button onClick={submit}>submit</button>
</div>
);
}
I am working on my first React program, it is the one provided by Sololearn (Contact Manager). I am trying to add a function to search for a contact: SearchName.
However, I need to click many times on the Search button for it to work. Can someone please tell me where I went wrong?
For example, typing James Smith in the enter a name to search field first gives "is not in list". Then when clicked again, it updates to is in list.
Here is the code:
import React, { useState } from "react";
import ReactDOM from "react-dom";
function AddPersonForm(props) {
const [person, setPerson] = useState("");
function handleChange(e) {
setPerson(e.target.value);
}
function handleSubmit(e) {
if (person !== "") {
props.handleSubmit(person);
setPerson("");
}
e.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Add new contact"
onChange={handleChange}
value={person}
/>
<button type="submit">Add</button>
</form>
);
}
function RemovePersonForm(props) {
const [person, setPerson] = useState("");
function handleChange(e) {
setPerson(e.target.value);
}
function handleSubmit(e) {
props.handleSubmit(person);
setPerson("");
e.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="enter name to delete"
onChange={handleChange}
/>
<button type="submit">Delete</button>
</form>
);
}
function PeopleList(props) {
const arr = props.data;
const listItems = arr.map((val, index) => <li key={index}>{val}</li>);
return <ul>{listItems}</ul>;
}
function SearchName(props) {
const [contacts, setContacts] = useState(props.data);
const [person, setPerson] = useState("");
const [isInList, setIsInList] = useState(false);
const [text, setText] = useState("");
function handleChange(e) {
setPerson(e.target.value);
}
function handleSubmit(e) {
setIsInList(false);
for (var c of contacts) {
if (c == person) {
setIsInList(true);
break;
}
}
if (isInList) {
setText("is in list");
} else {
setText("is not in list");
}
e.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="enter name to search"
onChange={handleChange}
/>
<button type="sumbit">Search</button>
<p>{text}</p>
</form>
);
}
function ContactManager(props) {
const [contacts, setContacts] = useState(props.data);
function addPerson(name) {
setContacts([...contacts, name]);
}
function removePerson(name) {
var newContacts = new Array();
var i = 0;
for (var c of contacts) {
if (c != name) {
newContacts[i] = c;
i++;
}
}
setContacts(newContacts);
}
return (
<div>
<AddPersonForm handleSubmit={addPerson} />
<RemovePersonForm handleSubmit={removePerson} />
<SearchName data={contacts} />
<PeopleList data={contacts} />
</div>
);
}
const contacts = ["James Smith", "Thomas Anderson", "Bruce Wayne"];
ReactDOM.render(
<ContactManager data={contacts} />,
document.getElementById("root"),
);
The root issue is that since setState is asynchronous, isInList hasn't had the time to change by the time you're checking it in handleSubmit.
However, since the printed text is strictly a function of whether isInList is true, it shouldn't be a separate state atom. If the computation was more complex, I'd recommend using useMemo for it.
On a similar note, you shouldn't "fork" the data prop to local contacts state.
Finally, you can simplify the finding procedure to a simple .find call instead of a loop.
function SearchName(props) {
const [person, setPerson] = useState("");
const [isInList, setIsInList] = useState(false);
function handleChange(e) {
setPerson(e.target.value);
}
function handleSubmit(e) {
setIsInList(props.data.find((c) => c === person));
e.preventDefault();
}
const text = isInList ? "is in list" : "is not in list";
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="enter name to search"
onChange={handleChange}
/>
<button type="sumbit">Search</button>
<p>{text}</p>
</form>
);
}
The problem is here
setIsInList(true);
This is an async method, meaning it will be updated after the whole function is executed. To solve this you can use useMemo method as follows (as expalained by #AKX. useEffect shouldnt be used)
const searchResult = useMemo(() => {
if (isInList) {
return "is in list";
} else {
return "is not in list";
}
}, [isInList]);
CodeSandbox here
It's because React states only update after an eventHandler is finished - that's something called batch update, In your case, you need to go "another round" to have it updated.
Here is my suggestion from your code:
Your modified sourcecode
Keep up the good learning!
For the Previous versions of react hook form, multiple forms used to work if you created another form object with the following lines of code:
const {
register: register2,
handleSubmit: handleSubmit2,
errors: errors2
} = useForm()
Now with the syntax changed to formState the errors object for the second form is not getting created, and it throws an error.
const {
register: register2,
handleSubmit: handleSubmit2,
formState: { errors2 }
} = useForm()
My Complete Code is here:
import { useForm } from 'react-hook-form'
import { useState } from 'react'
function App() {
const [firstname,setFirstName] = useState(null)
const [lastname,setLastName] = useState(null)
const [email,setEmail] = useState(null)
const [address,setAddress] = useState(null)
const { register, handleSubmit , formState: { errors } } = useForm()
const {
register: register2,
handleSubmit: handleSubmit2,
formState: { errors2 }
} = useForm()
const handleInputChange = (e) =>{
const target = e.target
const name = target.name
if(name === 'firstname'){
setFirstName(target.value)
}
if(name === 'lastname'){
setLastName(target.value)
}
if(name === 'email'){
setEmail(target.value)
}
if(name === 'address'){
setAddress(target.value)
}
}
const submitData = async()=>{
console.log(firstname,lastname)
}
const submitData2 = async()=>{
console.log(email,address)
}
return (
<div className="App">
<form key={1} onSubmit={handleSubmit(submitData)}>
<input name='firstname' type="text" {...register('firstname',{required:true})} onChangeCapture={handleInputChange} placeholder="enter firstname" />
<p>{errors.firstname && "First Name is Required"}</p>
<input name='lastname' type="text"{...register('lastname',{required:true})} onChangeCapture={handleInputChange} placeholder="enter lastname" />
<p>{errors.lastname && "Last Name is Required"}</p>
<button type="submit"> submit </button>
</form>
<hr />
<form key={2} onSubmit={handleSubmit2(submitData2)}>
<input name='email' type="text" {...register2('email',{required:true})} onChangeCapture={handleInputChange} placeholder="enter firstname" />
<p>{errors.email && " Email is Required"}</p>
<input name='address' type="text"{...register2('address',{required:true})} onChangeCapture={handleInputChange} placeholder="enter lastname" />
<p>{errors.address && "Address is Required"}</p>
<button type="submit"> submit </button>
</form>
</div>
);
}
export default App;
And this is my Output Screenshot:
The Errors get triggered for the first form, but for the second form, the errors don't display. react hook v7.5.2
Please Help !!!!
The problem here is the way you destructure the formState. Try this way and it should work:
const {
register: register2,
handleSubmit: handleSubmit2,
formState: { errors: errors2 }
} = useForm()
I want to make a multi-stage react form. In this particular code, only Zipcode will be asked and will be submitted on submitting the form. But how to bring other forms asking for Email, important thing~ I want the form for email after I submit Zipcode, as both email and zipcode will be sent to backend together.
import React, { useEffect, useState } from 'react';
const Rider_Signup = ()=>{
const [zipcode,setzipcode]=useState();
const [email,set_email]=useState();
const onSubmitform = async e =>{
e.preventDefault();
try{
const body={zipcode,email};
const response = await fetch("https://taxibackendf.herokuapp.com/api/service/signup",{
method:"POST",headers:{"Content-Type":"application/json"},
body:JSON.stringify(body)
})
const datainjson = await response.json();
window.location =`/driver/login`;
}catch(err){
console.log('Error')
}
}
return (
<div className="admin_form_div">
<form action="/initial" id="admin_form" name="admin_form" onSubmit={onSubmitform}
<input type="text" name="Zipcode" className="input" value={zipcode}
onChange={e =>setzipcode(e.target.value)}
/>
<button type="submit" className="confirm_btn" >Confirm</button>
</form>
</div>
);
};
export default Rider_Signup;
const [step, setstep] = useState(1);
const [formdata, setFormData] = useState({zip:"", email:""}); // use to hold input from user
const renderForm = () =>{
switch(step){
case 1: return <div>Form one with email<button onClick = {() => setstep(step+1)}>Submit</button></div>
case 2: return <div>Form with zip code <button onClick = {() => setstep(step+1)}>Submit</button></div>
default: return <div>Default case</div>
}
}
return (
renderForm()
)
`
Hello this is my first question here and I am just a beginner in Reactjs I need your explanation, please
the code is about Controlled Form wrote in-class component using "this.state".
I was trying time to turn it into a functional component using hooks with the same results
1- onSubmit render text on the screen
2- reset input into ""
the problem is no results going write and instead I got [object, Object] in the search
this is code
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
submit: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
handleSubmit(event) {
event.preventDefault()
this.setState({
submit: this.state.input,
input:''
})
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type='text'
value={this.state.input}
onChange={this.handleChange}
/>
<button type='submit'>Submit!</button>
</form>
<h1>{this.state.submit}</h1>
</div>
);
}
}
The code at codesandbox for fast access
please can you tell me how to solve it?
thank you
Here is what you need https://codesandbox.io/s/new-leftpad-1n0yy, you can compare your current MyForm with Form to understand the difference, but I suggest to check documentation deeper
Here is the answer by Artem Matiushenko he added the second component using useState, useCallback
now we can compare the two types for controlled form
import React, { useCallback, useState } from "react";
import "./styles.css";
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
input: "",
submit: "",
};
}
handleChange = (event) => {
this.setState({
input: event.target.value,
});
};
handleSubmit = (event) => {
event.preventDefault();
this.setState({
submit: this.state.input,
input: "",
});
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.input}
onChange={this.handleChange}
/>
<button type="submit">Submit!</button>
</form>
<h1>{this.state.submit}</h1>
</div>
);
}
}
//using hooks------->
function Form() {
const [value, setValue] = useState("");
const [submitedValue, setSubmitedValue] = useState();
const handleOnChange = useCallback(({ target }) => {
setValue(target.value);
}, []);
const handleOnSubmit = useCallback(
(event) => {
event.preventDefault();
setSubmitedValue(value);
setValue("");
},
[value]
);
return (
<div>
<form onSubmit={handleOnSubmit}>
<input type="text" value={value} onChange={handleOnChange} />
<button type="submit">Submit!</button>
</form>
<h1>{submitedValue}</h1>
</div>
);
}
export default function App() {
return (
<div className="App">
<h1>Class Component Form</h1>
<h2>controlled form</h2>
<MyForm />
<Form />
</div>
);
}
here is mine after I understood how useSate works
const MyForm = () => {
const [input, setInput] = useState("");
const [submitText, setSubmitText] = useState("");
const handleChange = (event) => {setInput(event.target.value)};
const handleSubmit = (event) => {
event.preventDefault();
setSubmitText(input);
setInput("");
};
return (
<div>
<form onSubmit={handleSubmit}>
<input type="text" value={input} onChange={handleChange} />
<button type="submit">Submit!</button>
</form>
<h1>{submitText}</h1>
</div>
);
};