I'm using react for my front-end. I have a signup form which uses formik which has following fileds - name, email, mobile, profile-picture. My profile picture is a file upload functional component imported into parent signup form component. Whenever I upload/change a picture, parent component rerenders which causes me to loose already filled formik field values.
What works instead is that when I remove profilePic from formik initialValues option.
But I want to use formik error handling and form submission so I want to set profilePic field value in formik itself.
In my FileUpload Functional Component, I let user crop the image to square and then upload it to server and get a url. This returned url ( see 'onDone={this.onPicUpload}' props ) is then send back to parent UserDetails Component and its profilePicUrl state is set (see 'onPicUpload' method)
CodeSandbox for your quick reference :: https://codesandbox.io/s/relaxed-pond-x8bgl?fontsize=14&hidenavigation=1&theme=dark
SignUp Form:
import React, { Component } from 'react';
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
import PasswordInput from '../components/PasswordInput';
// form constants
const strongRegex = new RegExp("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{8,})"); // for Password
const phoneRegExp = /^((\\+[1-9]{1,4}[ \\-]*)|(\\([0-9]{2,3}\\)[ \\-]*)|([0-9]{2,4})[ \\-]*)*?[0-9]{3,4}?[ \\-]*[0-9]{3,4}?$/ // for Mobile Numbers
const debug = true;
class UserDetails extends Component {
constructor(props) {
super(props);
this.state = {
...props,
profilePicUploaded: false,
profilePicUrl: null,
}
this.onPicUpload = this.onPicUpload.bind(this);
}
onPicUpload(url){ // imgUrl is a blob object's file value //imgUrl = blob.file
console.log(url)
this.setState({profilePicUrl: url})
};
render() {
let _this = this;
return(
<div id='userDetails'>
<p className='form-subHead'>Provide your basic details.</p>
<Formik
initialValues={{
firstName: 'FirstName',
lastName: 'LastName',
email: '[email protected]',
mobile: '1234567890',
password: 'Pass@1234',
profilePic: this.state.profilePicUrl,
}}
validationSchema={Yup.object().shape({
firstName: Yup.string()
.required('Please provide your name!')
.min(3, 'Is that a real name?'),
lastName: Yup.string()
.required('Please provide your name!')
.min(3, 'Is that a real name?'),
email: Yup.string()
.required("Please tell us your email.")
.email("Please enter a valid email."),
mobile: Yup.string()
.required("Please tell us your mobile number.")
.length(10, "Please enter a valid mobile number.")
.matches(phoneRegExp, "Please enter a valid mobile number."),
password: Yup.string()
.required("Please enter a password.")
.min(8, "Password is too short - minimum 8 characters.")
.matches(strongRegex, "Password must contain a number, a lowecase letter, a uppercase letter and a special character."),
profilePic: Yup.mixed().required("Please upload your profile picture"),
})}
onSubmit={(values, { resetForm, setErrors, setSubmitting, setFieldValue }) => {
setTimeout( () => {
console.log("Getting form values - ", values);
setSubmitting(false)
}, 500);
}}
enableReinitialize={true}
>
{props => {
const {
values,
touched,
dirty,
errors,
isSubmitting,
handleChange,
setFieldValue,
setFieldTouched
} = props;
return(
<Form noValidate >
<div className="row form-row">
<div className='col-sm-6'>
<div className="form-group">
<label htmlFor="firstName" className='form-label'>First Name</label>
<Field
type='text'
name='firstName' placeholder='Please provide your first name'
className='form-control'
/>
<ErrorMessage name="firstName" component="span" className="invalid-input" />
</div>{/* First Name */}
</div>
<div className='col-sm-6'>
<div className="form-group">
<label htmlFor="lastName" className='form-label'>Last Name</label>
<Field
type='text'
name='lastName' placeholder='Please provide your last name'
className='form-control'
/>
<ErrorMessage name="lastName" component="span" className="invalid-input" />
</div>{/* Last Name */}
</div>
</div>
<div className='row form-row'>
<div className='col-sm-6'>
<div className="form-group">
<label htmlFor="email" className='form-label'>Your Email</label>
<Field
type='email'
name='email' placeholder='Please provide your email'
className='form-control'
/>
<ErrorMessage name="email" component="span" className="invalid-input" />
</div>{/* Email */}
</div>
<div className='col-sm-6'>
<div className="form-group">
<label htmlFor="mobile" className='form-label'>Mobile Number</label>
<div className="inputGroup position-relative">
<span className="input-group-prepend countryCode position-absolute" id="india">+91</span>
<Field
type='number'
name='mobile' placeholder='Please provide your mobile Number'
className='form-control mobileNumber'
/>
</div>
<ErrorMessage name="mobile" component="span" className="invalid-input" />
</div>{/* Mobile */}
</div>
</div>
<div className='row form-row'>
<div className='col-sm-6'>
<div className="form-group">
<label htmlFor="password" className='form-label'>Password</label>
<div className="inputGroup position-relative">
<Field
type='password'
name='mobile' placeholder='Please provide a password'
className='form-control password'
/>
</div>
<ErrorMessage name="password" component="span" className="invalid-input" />
</div>{/* Password */}
</div>
<div className='col-sm-6'>
<div className='form-group'>
<label htmlFor="profilePic" className='form-label'>Upload Profile Picture</label>
<FileUpload id='profilePic' name='profilePic' //setFieldValue={setFieldValue}
for='profileImgHolder'
onDone={this.onPicUpload}
/>
<ErrorMessage name="profilePic" component="span" className="invalid-input" />
{this.state.fileUploadError && (
<span className='invalid-input'>{this.state.fileUploadError}</span>
)}
</div>
</div>
</div>
<div className='text-center'>
{isSubmitting ? <span className="loader-gif"><img src={loading} alt="Loading..." /></span> : null}
<button type="submit" className="btn btn-filled" disabled={!dirty || isSubmitting} >
Continue
</button>
{/*Submit */}
</div>
{debug && (
<>
<pre style={{ textAlign: "left" }}>
<strong>Values</strong>
<br />
{JSON.stringify(values, null, 2)}
</pre>
<pre style={{ textAlign: "left" }}>
<strong>Errors</strong>
<br />
{JSON.stringify(errors, null, 2)}
</pre>
</>
)}
</Form>
);
}}
</Formik>
</div>
)
}
}
export default UserDetails;