Search code examples
reactjsreduxreact-reduxredux-form

Redux-form FieldArray, handleSubmit' is not defined


I'm working to use a redux-form FieldArray. I'm having challenges connecting the form w the actual react component. When I try to submit the form, I get the following error: error 'handleSubmit' is not defined

The file is below. Am I building this form correctly in my React component? Any idea why handleSubmit is erroring?

import React from 'react';
import {Field, FieldArray, reduxForm} from 'redux-form'
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as inviteActions from '../../actions/inviteActions';

let renderEmailField = ({input, label, type, meta: {touched, error}}) =>
  <div>
    <label>{label}</label>
    <div>
      <input {...input} name="email" type="email"/>
      {touched && error && <span>{error}</span>}
    </div>
  </div>

let renderEmails = ({fields, meta: {submitFailed, error}}) =>
  <ul>
    <li>
      <button type="button" onClick={() => fields.push()}>Add Email</button>
    </li>
    {fields.map((email, index) =>
      <li key={index}>
        {index > 2 && <button
          type="button"
          title="Remove Email"
          onClick={() => fields.remove(index)}
        />}
        <Field
          name={email}
          component={renderEmailField}
          label={`Email #${index + 1}`}
        />
      </li>
    )}
    {submitFailed && error && <li className="error">{error}</li>}
  </ul>

let EmailsForm = ({handleSubmit, pristine, reset, submitting}) =>
  <form onSubmit={handleSubmit}>
    <FieldArray name="emails" component={renderEmails} />
    <div>
      <button type="submit" disabled={submitting}>Submit</button>
      <button type="button" disabled={pristine || submitting} onClick={reset}>
        Clear Values
      </button>
    </div>
  </form>


class InvitePage extends React.Component {

  handleSubmit(data) {
    console.log(data)
    this.props.actions.createInvites(data);
  }

  render() {

    return (
      <div>
        <h1>Invites</h1>
        <EmailsForm onSubmit={handleSubmit}/>
      </div>
    );
  }

}

EmailsForm = reduxForm({
  form: 'emailsForm',
  initialValues: {
    emails: ['', '', '']
  },
  validate(values) {
    const errors = {}
    if (!values.emails || !values.emails.length) {
      errors.emails = {_error: 'At least one email must be entered'}
    }
    else {
      let emailsArrayErrors = []
      values.emails.forEach((email, emailIndex) => {
        const emailErrors = {}
        if (email == null || !email.trim()) {
          emailsArrayErrors[emailIndex] = 'Required'
        }
      })
      if (emailsArrayErrors.length) {
        errors.emails = emailsArrayErrors
      }
    }
    return errors
  }
})(EmailsForm)

const mapStateToProps = state => {
  return {
    currentUser: state.currentUser
  };
};

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(inviteActions, dispatch)
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(InvitePage);

Solution

  • You have not provided onSubmit function as a prop to EmailsForm. You can pass it in two ways:

    EmailsForm = reduxForm({
      form: 'emailsForm',
      ...
      onSubmit: () => {}
      ... 
    })(EmailsForm)
    

    or you can pass onSubmit as a prop while calling EmailsForm

    <EmailsForm onSubmit={() => {}} />
    

    In your case, you have to write like this:

    <EmailsForm onSubmit={this.handleSubmit.bind(this)}/>
    

    According to me, if you can re-use these small components renderEmailField, renderEmails, EmailsForm, then you can create them as a separate component as you have done now.

    I would recommend, you should not separate EmailsForm from InvitePage class as you will have to pass all props from InvitePage to EmailsForm as your requirement grows. InvitePage is not serving any other purpose as of now other than passing onSubmit function.

    class InvitePage extends React.Component {
    
      handleSubmit = data => {
        console.log(data)
        this.props.actions.createInvites(data);
      }
    
      render() {
        const { pristine, reset, submitting } = this.props
        return (
          <div>
            <h1>Invites</h1>
            <form onSubmit={this.handleSubmit}> // react recommends we should not bind function here. Either bind that in constructor or write handleSubmit like I have written.
              <FieldArray name="emails" component={renderEmails} />
    
              <div>
                <button type="submit" disabled={submitting}>Submit</button>
                <button type="button" disabled={pristine || submitting} onClick={reset}>
                  Clear Values
                </button>
              </div>
            </form>
          </div>
        )
      }
    
    }
    
    InvitePage = reduxForm({
      form: 'emailsForm',
      initialValues: {
        emails: ['', '', ''],
      },
      validate(values) {
        ...
      }
    })(InvitePage)
    

    Hope it works.

    Try to use const rather than let if you are not changing the value of any variable.