1
votes

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;




1

1 Answers

0
votes

Try setting enableReinitialize to false.