Arif Mustaffa
Arif Mustaffa

Reputation: 163

React & Redux: "form submission canceled because the form is not connected"

I am still new in React and Redux. So, I know the existence of redux-form, but Im not intend to use in this project. So, what I am doing is creating a form without using redux-form. This form will grab the data from the reducers and pass it to backend API.

This is my main CreateListing.jsx page.

    // @flow
   import React from 'react';
   import { connect } from 'react-redux';
   import { Helmet } from 'react-helmet';
   import { SEOService } from '[services]';
   import CreateListingFormPage1 from './CreateListing/CreateListingFormPage1';
   import CreateListingFormPage2 from './CreateListing/CreateListingFormPage2';
   import CreateListingFormPage3 from './CreateListing/CreateListingFormPage3';
   import WhereAmI from './CreateListing/WhereAmI';
   import SuccessCreateListing from './CreateListing/SuccessCreateListing';

   type Props = {
    ...props...
  };

  class CreateListing extends React.Component<Props> {

getPageBySequence(pagenum) {
  // depending on whether User is logged in or not, show/hide the Login/Signup form which is Page3
  let sequence = [ CreateListingFormPage1, CreateListingFormPage2, CreateListingFormPage3 ];
  if (this.props.isLoggedIn) {
    sequence = [ CreateListingFormPage1, CreateListingFormPage2, CreateListingFormPage2 ];
  }
  return sequence[pagenum-1];
}

getSubmitCreateListing = (e) => {
  e.preventDefault();

  const propertyType = this.props.listingType;
  const propertyName = this.props.suggestedBuildings.selected;
  const propertyBuildingType = this.props.propertyBuildingType;
  const bedrooms = this.props.bed;
  const bathrooms = this.props.bath;
  const price = this.props.price;
  const builtUp = this.props.builtUp;
  const title = this.props.title;
  const tenure = this.props.tenure;
  const description = this.props.description;

  /* IN CASE USER NOT YET LOGGGED IN */
  if(this.props.isLoggedIn === false) {
    const email =  this.props.email;
    const password = this.props.password;

    this.props.cacheCreateListing({ email, password, propertyType, propertyName, propertyBuildingType, bedrooms, bathrooms, price, builtUp, title, tenure, description });
  }

  this.props.cacheCreateListing({ propertyType, propertyName, propertyBuildingType, bedrooms, bathrooms, price, builtUp, title, tenure, description });

  if(CreateListingFormPage1.landedTypes.includes(propertyBuildingType)) {
    this.props.geocodingRequired(true);
  }
  else {
    this.props.geocodingRequired(false);
  }
  this.props.onSubmitCreateListing();
}

onAuthenticateAndCreateListingButton() {
  if(this.props.isLoggedIn) {
    return(
      <div role="presentation">
        <div className={`column ${this.props.formCurrentPage === 1 ? '':'displayNone'}`}>
          <button type="button" className="Button button-next is-red" onClick={this.props.onNextClick}>
            NEXT
          </button>
        </div>
        <div className={`column ${this.props.formCurrentPage === 2 || this.props.formCurrentPage === 3 ? '':'displayNone'}`}>
          <button type="submit" className="Button button-create is-red" onClick={this.props.onLoadingCreateListing}>
            CREATE LISTING
          </button>
        </div>
      </div>
    )
  }
  return <div className={`column ${this.props.formCurrentPage < 3 ? '':'displayNone'}`}>
    <button type="button" className="Button button-next is-red" onClick={this.props.onNextClick}>
      NEXT
    </button>
  </div>
}

render() {
  if(this.props.isListingCreated){
    return <SuccessCreateListing />;
  }
  else if(this.props.isListingLoading){
    return <div className="create-listing-spinner" />
  }
  const CurrentPage = this.getPageBySequence(this.props.formCurrentPage);
  return (
    <div className={`CreateListing${this.props.isMobile ? '' : ' is-desktop'}`}>
      <Helmet>
        <title>{ SEOService.getMetaTitle('Create Property Listing') }</title>
        { SEOService.getCanonicalTag('/blogs') }
      </Helmet>
      <section className="CreateListing--Main">
        <div className="CreateListing--Container">
          <div className="CreateListing--WhereAmI">
            <WhereAmI page={this.props.formCurrentPage} />
          </div>
          <div className="CreateListing--Body">
            <form className="CreateListing--Form" onSubmit={ this.getSubmitCreateListing }>
              <CurrentPage />
              <div className='columns'>
                <div  className='column'/>
                {/* CHANGE THIS this.props.formCurrentPage < 3 later */}
                <div className={`column ${this.props.formCurrentPage > 1 && this.props.formCurrentPage < 4 ? '':'displayNone'}`}>
                  <button type="button" className="Button button-back" onClick={this.props.onPrevClick}>
                    BACK
                  </button>
                </div>
                { this.onAuthenticateAndCreateListingButton() }
                <div  className='column'/>
              </div>
            </form>
          </div>
        </div>
      </section>
    </div>
  );
}
  };

  const MapStateToProps = (state: State) => ({...});

  const MapDispatchToProps = (dispatch: Dispatch) => ({
   onLoadingCreateListing: () => dispatch({type: 'CREATE_LISTING_LOADING'}),
   onSubmitCreateListing: () => dispatch({type: 'CREATE_LISTING_SUBMIT_FORM'}),})

  export default connect(MapStateToProps,MapDispatchToProps)(CreateListing);

So, my <input type="text" /> are all from CreateListingFormPage1, CreateListingFormPage2 and CreateListingFormPage3 and put together in <CurrentPage />. My <form onSubmit={...}></form> is in this CreateListing.jsx page. Im not whether it is allowed to do it like this.

So, when I click submit, I got warning of Form submission canceled because the form is not connected.

My example of <input type="" /> in CreateListingFormPage1 are:

    // @flow
  import React from 'react';
  import { connect } from 'react-redux';
  import {Dropdown} from '[elements]';

  type Props = {...props...};

  class CreateListingFormPage2 extends React.Component<Props> {
    static get selectTenure() { return ["Select Tenure"].concat(this.tenureTypes) };
static get selectTitle() { return ["Select Title"].concat(this.titleTypes) };
static get selectBedroom() { return["Select Bedrooms no"].concat(this.bedroomNo) };
static get selectBathroom() { return["Select Bathrooms no"].concat(this.bathroomNo) };
static get tenureTypes(){
  return[
    "FREEHOLD",
    "LEASEHOLD",
    "OTHERS"
  ]};
  static get titleTypes(){
    return[
      "RESIDENTIAL",
      "COMMERCIAL",
      "INDUSTRIAL"
    ]};
  static get bedroomNo(){
    return[
      "1",
      "2",
      "3",
      "4",
      "5"
    ]};
  static get bathroomNo(){
    return[
      "1",
      "2",
      "3",
      "4",
      "5"
    ]};

get selectTenure() { return this.constructor.selectTenure; }
get selectTitle() { return this.constructor.selectTitle; }
get selectBedroom() { return this.constructor.selectBedroom; }
get selectBathroom() { return this.constructor.selectBathroom; }
get tenureTypes() { return this.constructor.tenureTypes; }
get titleTypes() { return this.constructor.titleTypes; }
get bedroomNo() { return this.constructor.bedroomNo; }
get bathroomNo() { return this.constructor.bathroomNo; }

hasInputError = (name) => {
  if (this.props.errors[name]) {
    return ' is-error';
  }
  return '';
}

render() {
  return (
    <div className={`Listing--Create${ this.props.isMobile ? '' : ' is-desktop' }`} id='form-second-page'>
      {/* <form className="Listing--form"> */}
        <div className="Listing--bedrooms-bathrooms">
          <div className="type-title">No. of Bedrooms</div>
          <Dropdown namespace="bedroom" selected={ this.selectBedroom[0] } options={ this.selectBedroom } onOptionSelect={ this.onBedroomDropdownSelect }/>

          <div className="type-title">Asking Price</div>
          <input className={`text-input price-input${ this.hasInputError('price')}`} type="text" onChange={ (e) => this.props.onPrice(e.currentTarget.value) } value={this.props.price} placeholder="RM"/>
        </div>

        <div className="Listing--price-built-up">
          <div className="type-title">No. of Bathrooms</div>
          <Dropdown namespace="bathroom" selected={ this.selectBathroom[0] } options={ this.selectBathroom } onOptionSelect={ this.onBathroomDropdownSelect }/>

          <div className="type-title">Built-up Size</div>
          <input className={`text-input built-up-input${ this.hasInputError('built_up_size')}`} type="text" onChange={ (e) => this.props.onBuiltUpSize(e.currentTarget.value) } value={this.props.builtUp} placeholder="sqft."/>
        </div>

        <div className="Listing--tenure">
          <div className="type-tenure">Select Tenure</div>
          <Dropdown namespace="tenure" selected={ this.selectTenure[0] } options={ this.selectTenure } onOptionSelect={ this.onTenureDropdownSelect }/>
        </div>

        <div className="Listing--title">
          <div className="type-title">Select Title</div>
          <Dropdown namespace="title" selected={ this.selectTitle[0] } options={ this.selectTitle } onOptionSelect={ this.onTitleDropdownSelect }/>
        </div>

        <div className="Listing--photos">
          <div className="type-title">Upload Photos</div>
          <button className={`text-input photos-input${ this.hasInputError('photos')}`}>Click to upload</button>
        </div>

        <div className="Listing--description">
          <div className="type-title">Describe your property</div>
          <textarea className={`text-input description-input${ this.hasInputError('description')}`} onChange={ (e) => this.props.onDescription(e.currentTarget.value) } value={this.props.description} placeholder="Describe your property"/>
        </div>
    </div>
  );
}
  };


  const MapStateToProps = (state: State) => ({
   ...
  });

  const MapDispatchToProps = (dispatch: Dispatch) => ({
   ...
  })

  export default connect(MapStateToProps, MapDispatchToProps)(CreateListingFormPage2);

Basically, there is nothing wrong with my redux store. All the value of the input is captured successfully. The problem is when submitting the form, either the onSubmit or my form structure method is incorrect.

This is CreateListing.js reducer should it be helpful:

const INITIAL_STATE= {
 isListingLoading: false,
 isListingCreated: false,
}
const CreateListing = (state = INITIAL_STATE, action) => {
 switch(action.type){
 case 'CREATE_LISTING_LOADING':
        return Object.assign({}, state, {isListingLoading: true});
 case 'CREATE_LISTING_SUBMIT_FORM':
        return Object.assign({}, state, {isListingCreated: true});
 default:
        return state;
} }
 export default CreateListing;

Any help is much appreciated.

Upvotes: 1

Views: 4081

Answers (1)

Amruth
Amruth

Reputation: 5912

if you have any other buttons in your form you should add type="button". so make changes like this.

      <button type="button" className="Button button-create is-red" onClick={this.props.onLoadingCreateListing}>
        CREATE LISTING
      </button>

Upvotes: 7

Related Questions