React state update input data - reactjs

I have developed react application, with router which has 2 routes. In one Page I am displaying the value, in the other page I have one form which has two input fields(Email and Phone) and one button. I have Onchange event for each input field. as soon as I do changes in input field and without clicking on update button, if I go back to the first page the data is updating without submitting. But after refresh it will get back to original value.
Can you please help me out, How to avoid the updating data.
Thanks in advance.
Here is my code:
import NumberFormat from 'react-number-format'
import {
useForm,
Controller,
ErrorMessage,
} from 'react-hook-form/dist/react-hook-form.ie11'
import isValidEmailAddress from '../utility/isValidEmailAddress'
import Messages from './Messages'
import Button from './Button'
import { useLocation } from 'react-router-dom'
export default function EditContactInformation({
changeContactInfoSmall,
contactInfo,
updateContactInfo,
}) {
const { handleSubmit, register, control, errors } = useForm()
return (
<form onSubmit={handleSubmit(() => changeContactInfo(contactInfo))}>
<h3>Edit contact information</h3>
<Messages results={contactInfo} />
<div className="edit-contact-information__form-col">
<div className="form-group form-group--email">
<label htmlFor="email">Preferred email address</label>
<input
name="email"
id="email"
type="text"
ref={register({
required: 'Please enter an email address',
validate: (value) =>
isValidEmailAddress(value) ||
'Please enter a valid email address',
})}
onChange={(e) => {
updateContactInfo('email', e.currentTarget.value)
return e.currentTarget.value
}}
defaultValue={contactInfo.get('email')}
/>
<ErrorMessage
errors={errors}
name="email"
as={<div className="form-errors" />}
/>
</div>
<div className="form-group form-group--phone">
<label htmlFor="phone">Preferred phone</label>
<Controller
as={<NumberFormat id="phone" format="(###) ###-####" mask="_" />}
name="phone"
rules={{
required: 'Please enter a phone number',
minLength: {
value: 10,
message: 'Please enter a valid phone number',
},
}}
onChangeName="onValueChange"
onChange={([{ value }]) => {
updateContactInfo('phone', value)
return value
}}
control={control}
defaultValue={contactInfo.get('phone')}
/>
<ErrorMessage
errors={errors}
name="phone"
as={<div className="form-errors" />}
/>
</div>
</div>
<div className="form-group">
<label>
<input
type="checkbox"
name="updateAll"
data-testid="update-all-button"
onChange={(e) => {
updateContactInfo('updateAll', e.currentTarget.checked)
}}
ref={register()}
/>{' '}
Update my contact information for all properties
</label>
</div>
<Button
className="button--primary"
arrow
type="submit"
isLoading={contactInfo.get('submitting')}
>
Update contact
</Button>
</form>
)
}
EditContactInformation.propTypes = {
changeContactInfo: PropTypes.func.isRequired,
contactInfo: PropTypes.object.isRequired,
updateContactInfo: PropTypes.func.isRequired,
}

you should use session storage for this situation. React states are deleted when the page is refreshed due to its nature. You should keep the information you want to keep session storage.

States in react are non persistent, meaning that data stored in state is purged when the site is refreshed or navigated out of. So, if you want to hold on to your data, depending on your need, you can either use cookies or your browser's local storage to persist.
react-persist is a great library that you can use to achieve this using localStorage.

Related

Why defaultValue from React hook form can not work in react

I stuck in a problem. I am using React Hook Form
in my shipment component everything has gone well but while i try to use react hook form for my shipment component and setting default value on these component it's not work accurately. My code is given below:
import React, { useContext } from 'react';
import { useForm } from 'react-hook-form';
import { UserContext } from '../../App';
import './Shipment.css';
const Shipment = () => {
const { register, handleSubmit, watch, formState: { errors } } =
useForm();
const [loggedInUser,setLoggedInUser] = useContext(UserContext);
const onSubmit = data => {
console.log('form submitted',data);}
console.log(watch("example")); // watch input value by passing the name
of it
return (
<form className="ship-form" onSubmit={handleSubmit(onSubmit)}>
{/* <input defaultValue={loggedInUser.email}
{...register("example")} /> */}
<input name="name" defaultValue={loggedInUser.name}
{...register("exampleRequired", { required: true })} placeholder="Your
Name" />
{errors.name && <span className="error">Name is required</span>}
<input name="email" defaultValue={loggedInUser.email}
{...register("exampleRequired", { required: true })}
placeholder="Email address"/>
{errors.email && <span className="error">Email is required</span>}
<input name="address" {...register("exampleRequired", { required:
true })} placeholder="address"/>
{errors.address && <span className="error">Address is
required</span>}
<input name="phone" {...register("exampleRequired", { required: true
})} placeholder="Phone number"/>
{errors.phone && <span className="error">Phone Number is
required</span>}
<input type="submit" />
</form>
);
};
export default Shipment;
when i attempt to sign in by using email :
i set default value on name and email field but it's not work except name
and my console show only name except all data
Actually i need all data and set default value on a specific field
I think the problem is here:
Before
<input name="email" defaultValue={loggedInUser.email}
{...register("exampleRequired", { required: true })}
placeholder="Email address"/>
After:
<input defaultValue={loggedInUser.email}
{...register("email", { required: true })}
placeholder="Email address"/>
All of your register have the same name, that's why it doesn't work.
See the register API

How to get data from form component and pass to method in React?

In a React project, I have form component which gets input data like email, password and passes to submit method. In a method 'requestOTP' requires email from form component. What could be appropriate solution to get that email data from form and pass to requestOTP method? Below is the code for reference.
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
control={control}
name="email"
render={({ onChange, value, ref }) => (
<Input
placeholder="EMAIL"
onChange={onChange}
ref={ref}
value={value}
type="email"
/>
)}
rules={{
required: "Please enter email",
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: "Invalid email address"
}
}}
/>
<div className={classes2.root}>
<Button
variant="contained"
type="submit"
>
LOG IN
</Button>
</div>
</form>
<form onSubmit={handleSubmit(requestOtp)}>
<Button
variant="contained"
type="submit"
>
GET AN OTP ON YOUR EMAIL
</Button>
</form>
const requestOtp = async (data) => {
{/* I want email data from form component here */}
}
onSubmit event listener already have target values. For example, If you have input tag named 'email' in your form (that is <input name="email" ... />)
function handleSubmit(e) {
const email = e.target['email'].value;
//... and then pass email to the requestOTP function
}

Conditional validation with react hook form

Here is my form looks like and also CodeSanbox. currently I'm using react-hook-form
as you can see form has 3 inputs. Submit button should be disabled until all the required fields are entered.
Two use case:
If "Check" is unchecked:
only "id" should be validated and submit button should get enabled. "firt" and "last" names should not be part of form data
If "Check" is checked
all the fields should be validated
first and last names are only required if "Check" is checked. so its not checked then form should only validate "ID" field. if "Check" is checked then all fields should get validated.
problem I'm having is if I enter id, form state is still "invalid". Form is expecting to enter values for first and last name.
I would appreciate any help.
I have updated your CodeSanBox code and also adding the full code here:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import "./index.css";
function App() {
const {
register,
handleSubmit,
errors,
formState,
unregister,
setValue,
getValues,
reset
} = useForm({
mode: "onBlur",
reValidateMode: "onBlur",
shouldUnregister: true
});
//console.log(formState.isValid);
console.log(errors);
const [disabled, setDisabled] = useState(true);
const onSubmit = (data) => {
alert(JSON.stringify(data));
};
useEffect(() => {
// #ts-ignore
if (disabled) {
console.log("unregister");
reset({ ...getValues(), firstName: undefined, lastName: undefined });
unregister(["firstName", "lastName"]);
} else {
console.log("register");
register("firstName", { required: true });
register("lastName", { required: true });
}
}, [disabled]);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor="id">ID</label>
<input
name="id"
placeholder="id"
ref={register({ required: true, maxLength: 50 })}
/>
{errors.id && <p>"ID is required"</p>}
<fieldset disabled={disabled}>
<legend>
<input
type="checkbox"
name={"name"}
ref={register}
onClick={() => setDisabled(!disabled)}
/>
<span>Check</span>
</legend>
<label htmlFor="firstName">First Name</label>
<input
name="firstName"
placeholder="Bill"
onChange={(e) => {
console.log(e.target.value);
setValue("firstName", e.target.value);
}}
ref={register({ required: !disabled })}
/>
{errors.firstName && <p>"First name is required"</p>}
<label htmlFor="lastName">Last Name</label>
<input
name="lastName"
placeholder="Luo"
onChange={(e) => setValue("lastName", e.target.value)}
ref={register({ required: !disabled })}
/>
{errors.lastName && <p>"Last name is required"</p>}
</fieldset>
<input type="submit" disabled={!formState.isValid} />
</form>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
First I found that you set disabled state as false which should be true as an initial value, and regarding the issue, I have used reset and getValues functions when the disabled state changes.
EDIT for you to recognize code changes easy, I have restored all the code at CodeSanBox.
This whole validation behavior (UX) is definitely making things a bit harder, however, there are a couple of things that you should leverage from the library such as:
watch
validate
getValues
import React from "react";
import ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import "./index.css";
function App() {
const {
register,
handleSubmit,
errors,
formState: { isValid, touched },
getValues,
trigger,
watch
} = useForm({
mode: "onBlur"
});
const onSubmit = (data) => {
alert(JSON.stringify(data));
};
const validate = (value) => {
if (getValues("name")) { // read the checkbox value
return !!value;
}
return true;
};
const isChecked = watch("name"); // watch if the name is checked
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor="id">ID</label>
<input
name="id"
placeholder="id"
ref={register({ required: true, maxLength: 50 })}
/>
{errors.id && <p>"ID is required"</p>}
<fieldset disabled={!isChecked}>
<legend>
<input
type="checkbox"
name={"name"}
ref={register}
onChange={() => trigger()} // you want update isValid due to state change, and also those extra two inputs become required
/>
<span>Check</span>
</legend>
<label htmlFor="firstName">First Name</label>
<input
name="firstName"
placeholder="Bill"
ref={register({
validate
})}
/>
// make sure input is touched before fire an error message to the user
{errors.firstName && touched["firstName"] && (
<p>"First name is required"</p>
)}
<label htmlFor="lastName">Last Name</label>
<input
name="lastName"
placeholder="Luo"
ref={register({
validate
})}
/>
{errors.lastName && touched["lastName"] && (
<p>"Last name is required"</p>
)}
</fieldset>
<input type="submit" disabled={!isValid} />
</form>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
CSB:
https://codesandbox.io/s/react-hook-form-conditional-fields-forked-n0jig?file=/src/index.js:0-1831
on your ref, dont use hard coded bool true, ref={register({ required: true})}, but your dynamic ref={register({ required: disabled })}
do notice that because your mode: "onBlur" config, the button won't be abled until id field blurred
You just need to replace true .from ref: required:true..... Instead use const 'disabled' ....in input of first and last name .
So as to achieve dynamic change

ReactJS: Validation not working with Yup using Formik multistep form

I have a multi steps form in which i have used a Formik and Yup libraries.
But the validation that i am using of yup library is not working at all. In React debugger tool i am getting it's value as empty. So, whatever i write in the input field it validates with "Required" message as it's value is empty.
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';
let accountInfoSchema = Yup.object().shape({
name: Yup.string()
.max(20, 'Too Long!')
.required('Name is Required'),
});
class AccountInfo extends Component {
handleChange = event => {
this.setState({
userAccountData: Object.assign(
{},
this.state.userAccountData,
{ [event.target.name]: event.target.value }
),
})
}
AccountInfoView = () => {
return (
<section className="form">
<React.Fragment>
<Formik
initialValues={{name:'' }}
validationSchema={accountInfoSchema}
render={() => {
return(
<Form onSubmit={this.handleSubmit}>
{this.Step1()}
{this.Step2()}
{this.Step3()}
<li className="stb_btn half">
<div className="custom_group">
{this.previousButton()}
</div>
</li>
<li className="stb_btn half">
<div className="custom_group">
{this.nextButton()}
</div>
</li>
</Form>
);
}}
/>
</React.Fragment>
</div>
</section>
)
}
Step1 = () => {
return(
<li>
<div className="custom_group">
<Field type="text" name="name" className="trans text_input" placeholder="Enter your name" value={this.state.userAccountData['name']} onChange={this.handleChange} />
<label className="label_two">Name</label>
<ErrorMessage name="name" />
</div>
</li>
)
}
render() {
return (
<div>{this.AccountInfoView()}</div>
)
}
}
please review the react console response for the value as empty.
The reason why it isn't validating is because you are passing to the Field this.state.userAccountData['name'] and onChange={this.handleChange}.
Formik already have a state to store all of the data from your form, you don't need to keep it in the components state.
When you add onChange={this.handleChange} to the field, you change the component's state, but don't change the Formik's state, this is why the validation isn't triggering.
I'm not sure why you are keeping the name in the state, but if you don't have any reason why these are unecessary.
// formik already handle the state for you inside `values`
handleChange = event => {
this.setState({
userAccountData: Object.assign(
{},
this.state.userAccountData,
{ [event.target.name]: event.target.value }
),
})
}
// Formik already handles the value and the onChange of the input
<Field type="text" name="name" className="trans text_input" placeholder="Enter your name" value={this.state.userAccountData['name']} onChange={this.handleChange} />
The only thing you need is to set the field's name prop, so it matches the validation.
// this is enough
<Field type="text" name="name" className="trans text_input" placeholder="Enter your name" />

Submitting redux form values

I am new to react and redux technology. now started building an application that contains several redux forms. We want to submit simple form with values.
For ex: login form
Username : text input field
Password: text input field
Submit button
After entering values in fields and click on submit button i want to get the username and password field values in object or json data .. so that I can store it to my server with POST method.
Right now we are using handleSubmit(), but data is not coming as object
1 - The best practice to deal with input values are making them controlled. Which means :
Instead of
<input type='password' />
You do :
<input
type='password'
value={password}
onChange={ event => myInputHandler( event.target.value ) }
/>
The value might come from your state, redux state or as a props etc.
Your handler function differs according to where you store it.
I will give you an example with react state :
<input
type='password'
value={this.state.password}
onChange={ event => this.setState({ password : event.target.value }) }
/>
So whenever someone types, your onChange handler will be called, so that your react state will update with the input ( event.target.value ).
2 - If you need these values when a user submits, then you need to wrap these input fields within a form element and attach a onSubmit handler.
onSubmitHandler( event ){
event.preventDefault()
let password = this.state.password
// use password or other input fields, send to server etc.
}
<form onSubmit={ event => this.onSubmitHandler(event) }>
<input
type='password'
value={this.state.password}
onChange={ event => this.setState({ password : event.target.value }) }
/>
</form>
Hope you get what you need.
If you are using redux to store state then use redux-from then use redux from
import React from 'react'
import {Field, reduxForm} from 'redux-form'
const SimpleForm = props => {
const {handleSubmit, submitting} = props return (
<form onSubmit={handleSubmit(e=>console.log('your form detail here', e))}>
<div>
<label>First Name</label>
<div>
<Field name="firstName" component="input" type="text" placeholder="First Name" />
</div>
</div>
<div>
<label>Last Name</label>
<div>
<Field name="lastName" component="input" type="text" placeholder="Last Name" />
</div>
</div>
<div>
<button type="submit" disabled={pristine || submitting}>Submit</button>
</div>
</form>
) }
export default reduxForm({ form: 'simple'})(SimpleForm)
Go here for more detail
https://redux-form.com
I put the name of the input as the key that I want to use.
Then each time the input changes I destructure the event passed to the onChange function, and I use the name,value to update the state.
On form submit make sure to use preventDefault(); in order to avoid the page refreshing.
import React, { Component } from 'react'
class FormExample extends Component {
constructor(props){
super(props)
this.state = {
formData: {}
}
}
handleInputChange = ({ target: { name,value } }) => {
this.setState({
formData: {
...this.state.formData,
[name]: value
}
})
}
handleFormSubmit = e => {
e.preventDefault()
// This is your object
console.log(this.state.formData)
}
render() {
return(
<div>
<Form
handleSubmit={this.handleFormSubmit}
handleChange={this.handleInputChange}
/>
</div>
)
}
}
const Form = ({ handleSubmit, handleChange }) => (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} name="username" type="text" placeholder="Username" />
<input onChange={handleChange} name="password" type="password" placeholder="Password" />
<button>Submit</button>
</form>
)
export default FormExample

Resources