title: React & Redux: "form submission canceled because the form is not connected"
body: <p>I am still new in React and Redux. So, I know the existence of <code>redux-form</code>, but Im not intend to use in this project. So, what I am doing is creating a <code>form</code> without using <code>redux-form</code>. This <code>form</code> will grab the data from the reducers and pass it to <code>backend API</code>.  </p>
  
  <p>This is my main <code>CreateListing.jsx</code> page. </p>
  
  <pre><code>    // @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&lt;Props&gt; {
  
  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) =&gt; {
    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(
        &lt;div role="presentation"&gt;
          &lt;div className={`column ${this.props.formCurrentPage === 1 ? '':'displayNone'}`}&gt;
            &lt;button type="button" className="Button button-next is-red" onClick={this.props.onNextClick}&gt;
              NEXT
            &lt;/button&gt;
          &lt;/div&gt;
          &lt;div className={`column ${this.props.formCurrentPage === 2 || this.props.formCurrentPage === 3 ? '':'displayNone'}`}&gt;
            &lt;button type="submit" className="Button button-create is-red" onClick={this.props.onLoadingCreateListing}&gt;
              CREATE LISTING
            &lt;/button&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      )
    }
    return &lt;div className={`column ${this.props.formCurrentPage &lt; 3 ? '':'displayNone'}`}&gt;
      &lt;button type="button" className="Button button-next is-red" onClick={this.props.onNextClick}&gt;
        NEXT
      &lt;/button&gt;
    &lt;/div&gt;
  }
  
  render() {
    if(this.props.isListingCreated){
      return &lt;SuccessCreateListing /&gt;;
    }
    else if(this.props.isListingLoading){
      return &lt;div className="create-listing-spinner" /&gt;
    }
    const CurrentPage = this.getPageBySequence(this.props.formCurrentPage);
    return (
      &lt;div className={`CreateListing${this.props.isMobile ? '' : ' is-desktop'}`}&gt;
        &lt;Helmet&gt;
          &lt;title&gt;{ SEOService.getMetaTitle('Create Property Listing') }&lt;/title&gt;
          { SEOService.getCanonicalTag('/blogs') }
        &lt;/Helmet&gt;
        &lt;section className="CreateListing--Main"&gt;
          &lt;div className="CreateListing--Container"&gt;
            &lt;div className="CreateListing--WhereAmI"&gt;
              &lt;WhereAmI page={this.props.formCurrentPage} /&gt;
            &lt;/div&gt;
            &lt;div className="CreateListing--Body"&gt;
              &lt;form className="CreateListing--Form" onSubmit={ this.getSubmitCreateListing }&gt;
                &lt;CurrentPage /&gt;
                &lt;div className='columns'&gt;
                  &lt;div  className='column'/&gt;
                  {/* CHANGE THIS this.props.formCurrentPage &lt; 3 later */}
                  &lt;div className={`column ${this.props.formCurrentPage &gt; 1 &amp;&amp; this.props.formCurrentPage &lt; 4 ? '':'displayNone'}`}&gt;
                    &lt;button type="button" className="Button button-back" onClick={this.props.onPrevClick}&gt;
                      BACK
                    &lt;/button&gt;
                  &lt;/div&gt;
                  { this.onAuthenticateAndCreateListingButton() }
                  &lt;div  className='column'/&gt;
                &lt;/div&gt;
              &lt;/form&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/section&gt;
      &lt;/div&gt;
    );
  }
    };
  
    const MapStateToProps = (state: State) =&gt; ({...});
  
    const MapDispatchToProps = (dispatch: Dispatch) =&gt; ({
     onLoadingCreateListing: () =&gt; dispatch({type: 'CREATE_LISTING_LOADING'}),
     onSubmitCreateListing: () =&gt; dispatch({type: 'CREATE_LISTING_SUBMIT_FORM'}),})
  
    export default connect(MapStateToProps,MapDispatchToProps)(CreateListing);
  </code></pre>
  
  <p>So, my <code>&lt;input type="text" /&gt;</code> are all from <code>CreateListingFormPage1</code>, <code>CreateListingFormPage2</code> and <code>CreateListingFormPage3</code> and put together in <code>&lt;CurrentPage /&gt;</code>. My <code>&lt;form onSubmit={...}&gt;&lt;/form&gt;</code> is in this <code>CreateListing.jsx</code> page. Im not whether it is allowed to do it like this.</p>
  
  <p>So, when I click submit, I got warning of <code>Form submission canceled because the form is not connected</code>. </p>
  
  <p>My example of <code>&lt;input type="" /&gt;</code> in <code>CreateListingFormPage1</code> are:</p>
  
  <pre><code>    // @flow
    import React from 'react';
    import { connect } from 'react-redux';
    import {Dropdown} from '[elements]';
  
    type Props = {...props...};
  
    class CreateListingFormPage2 extends React.Component&lt;Props&gt; {
      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) =&gt; {
    if (this.props.errors[name]) {
      return ' is-error';
    }
    return '';
  }
  
  render() {
    return (
      &lt;div className={`Listing--Create${ this.props.isMobile ? '' : ' is-desktop' }`} id='form-second-page'&gt;
        {/* &lt;form className="Listing--form"&gt; */}
          &lt;div className="Listing--bedrooms-bathrooms"&gt;
            &lt;div className="type-title"&gt;No. of Bedrooms&lt;/div&gt;
            &lt;Dropdown namespace="bedroom" selected={ this.selectBedroom[0] } options={ this.selectBedroom } onOptionSelect={ this.onBedroomDropdownSelect }/&gt;
  
            &lt;div className="type-title"&gt;Asking Price&lt;/div&gt;
            &lt;input className={`text-input price-input${ this.hasInputError('price')}`} type="text" onChange={ (e) =&gt; this.props.onPrice(e.currentTarget.value) } value={this.props.price} placeholder="RM"/&gt;
          &lt;/div&gt;
  
          &lt;div className="Listing--price-built-up"&gt;
            &lt;div className="type-title"&gt;No. of Bathrooms&lt;/div&gt;
            &lt;Dropdown namespace="bathroom" selected={ this.selectBathroom[0] } options={ this.selectBathroom } onOptionSelect={ this.onBathroomDropdownSelect }/&gt;
  
            &lt;div className="type-title"&gt;Built-up Size&lt;/div&gt;
            &lt;input className={`text-input built-up-input${ this.hasInputError('built_up_size')}`} type="text" onChange={ (e) =&gt; this.props.onBuiltUpSize(e.currentTarget.value) } value={this.props.builtUp} placeholder="sqft."/&gt;
          &lt;/div&gt;
  
          &lt;div className="Listing--tenure"&gt;
            &lt;div className="type-tenure"&gt;Select Tenure&lt;/div&gt;
            &lt;Dropdown namespace="tenure" selected={ this.selectTenure[0] } options={ this.selectTenure } onOptionSelect={ this.onTenureDropdownSelect }/&gt;
          &lt;/div&gt;
  
          &lt;div className="Listing--title"&gt;
            &lt;div className="type-title"&gt;Select Title&lt;/div&gt;
            &lt;Dropdown namespace="title" selected={ this.selectTitle[0] } options={ this.selectTitle } onOptionSelect={ this.onTitleDropdownSelect }/&gt;
          &lt;/div&gt;
  
          &lt;div className="Listing--photos"&gt;
            &lt;div className="type-title"&gt;Upload Photos&lt;/div&gt;
            &lt;button className={`text-input photos-input${ this.hasInputError('photos')}`}&gt;Click to upload&lt;/button&gt;
          &lt;/div&gt;
  
          &lt;div className="Listing--description"&gt;
            &lt;div className="type-title"&gt;Describe your property&lt;/div&gt;
            &lt;textarea className={`text-input description-input${ this.hasInputError('description')}`} onChange={ (e) =&gt; this.props.onDescription(e.currentTarget.value) } value={this.props.description} placeholder="Describe your property"/&gt;
          &lt;/div&gt;
      &lt;/div&gt;
    );
  }
    };
  
  
    const MapStateToProps = (state: State) =&gt; ({
     ...
    });
  
    const MapDispatchToProps = (dispatch: Dispatch) =&gt; ({
     ...
    })
  
    export default connect(MapStateToProps, MapDispatchToProps)(CreateListingFormPage2);
  </code></pre>
  
  <p>Basically, there is nothing wrong with my <code>redux</code> store. All the value of the <code>input</code> is captured successfully. The problem is when submitting the form, either the <code>onSubmit</code> or my form structure method is incorrect.</p>
  
  <p>This is <code>CreateListing.js</code> reducer should it be helpful:</p>
  
  <pre><code>const INITIAL_STATE= {
   isListingLoading: false,
   isListingCreated: false,
  }
  const CreateListing = (state = INITIAL_STATE, action) =&gt; {
   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;
  </code></pre>
  
  <p>Any help is much appreciated. </p>