Search code examples
reactjsreduxreact-reduxredux-form

Retrieve select value with redux-form


I am quite a beginner so don't be offensed by my question, it must be something stupid but I have been stuck for 2 long, trying so much things I found when searching.

My problem is simple as hell, I use React and redux with the redux-form library. My form is quite simple and works for all the fields except... my combobox for which I don't manage to retrieve any data. It is like my Field select component was not visible from my react component which does not seem to react to its state etc.

Here is my code (3 different files) :

Shared utility functions file

export const renderSelectField = (field) => {
  var styles = {};
  var containerStyle = getInputStylesContainer();

  if (field.input.value || field.meta.touched) {
    if (!field.meta.error) {
      styles = getInputStylesSuccess();
      containerStyle = classNames(containerStyle, {'has-success': true});
    } else {
      styles = getInputStylesError();
      containerStyle = classNames(containerStyle, {'has-error': true});
    }
  }

  return (<div className={containerStyle}>
    {displayInputLabel(styles.idInput, field.label)}
    <select className='form-control' name={field.input.name} id={styles.idInput} aria-describedby={styles.ariaDescribedBy}>
      {field.options}
    </select>
    <span className={styles.glyphicon} aria-hidden='true' />
    {field.meta.touched && field.meta.error &&
    displayErrorMessage(field.meta.error)}
  </div>);
};

The container for the component

export class ProfileContainer extends React.Component {
  render () {
    // TODO: Check user connection (via the store or via a check within the cookie ?
    // TODO: Retrieve the proper userId (via mapStateToProps
    if (this.props.connected === false) browserHistory.push('/');
    return <ProfileForm
      user={this.props.user}
      fields={this.props.fields}
      errorMessage={this.props.errorMessage}
      confirmationMessage={this.props.confirmationMessage}
      onSubmitProfileUpdate={this.props.onSubmitProfileUpdate} />;
  }
}

ProfileContainer.propTypes = {
  user: React.PropTypes.object,
  connected: React.PropTypes.bool,
  fields: React.PropTypes.shape({
    email: React.PropTypes.string,
    firstname: React.PropTypes.string,
    lastname: React.PropTypes.string,
    ranking: React.PropTypes.string,
    telephone: React.PropTypes.string,
    city: React.PropTypes.string
  }),
  errorMessage: React.PropTypes.string,
  confirmationMessage: React.PropTypes.string,
  onSubmitProfileUpdate: React.PropTypes.func.isRequired
};

const mapStateToProps = (state) => {
  return {
    connected: state.userConnection.connection.connected,
    user: state.userConnection.loadProfile.user,
    errorMessage: state.profile.error,
    confirmationMessage: state.profile.confirmation
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    onSubmitProfileUpdate: (values) => {
      const user = {
        email: values.email,
        telephone: values.telephone,
        firstname: values.firstname,
        lastname: values.lastname,
        city: values.city,
        ranking: values.ranking
      };
      console.log(values);
      console.log('RANKING', user.ranking);
      dispatch(profileUpdate(user));
    }
  };
};

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

Component with the form

class ProfileForm extends React.Component {

  getTennisRankingsOptions (rankings) {
    return (
      tennisRankings.map(ranking =>
        <option value={ranking} key={ranking}>{ranking}</option>)
    );
  }
  render () {
    const { handleSubmit } = this.props;
    const messageClassname = this.props.errorMessage !== undefined ? stylesShared.errorMessage : this.props.confirmationMessage !== undefined ? stylesShared.confirmationMessage : '';
    return (
      <div>
        <div>
          <div>
            <form onSubmit={handleSubmit(this.props.onSubmitProfileUpdate)}>
              <div>
                <center><h4>Votre profil</h4></center>
              </div>
              <div className={messageClassname}>
                {this.props.errorMessage &&
                <span>{this.props.errorMessage}</span>
                }
                {this.props.confirmationMessage &&
                <span>{this.props.confirmationMessage}</span>
                }
              </div>
              <div>
                <Field name='firstname' type='text' label='Prénom' component={renderBasicField} />
              </div>
              <div>
                <Field name='lastname' type='text' label='Nom' component={renderBasicField} />
              </div>
              <div>
                <Field name='email' type='email' label='Email' component={renderEmailField} />
              </div>
              <div>
                <Field name='telephone' type='text' label='Téléphone' component={renderBasicField} />
              </div>
              <div>
                <Field name='ranking' className='input-row form-group form-control' options={this.getTennisRankingsOptions()} type='select' component={renderSelectField} />
              </div>
              <div>
                <Field name='city' type='text' label='Ville' component={renderBasicField} />
              </div>
              <div>
                <button className='btn btn-info btn-lg center-block' type='submit'>Mettre à jour</button>
              </div>
            </form>
          </div>
        </div>
      </div>
    );
  }
}

ProfileForm.propTypes = {
  user: React.PropTypes.object,
  fields: React.PropTypes.shape({
    firstname: React.PropTypes.string,
    lastname: React.PropTypes.string,
    email: React.PropTypes.string,
    telephone: React.PropTypes.string,
    ranking: React.PropTypes.string,
    city: React.PropTypes.string
  }),
  errorMessage: React.PropTypes.string,
  confirmationMessage: React.PropTypes.string,
  onSubmitProfileUpdate: React.PropTypes.func.isRequired,
  handleSubmit: propTypes.handleSubmit,
  initialize: propTypes.initialize
};

const validateProfileForm = values => {
  const errors = {};

  if (!values.firstname) errors.firstname = 'Un prénom est requis';
  else if (validator.isLength(values.firstname + '', {min: 0, max: 1})) errors.firstname = 'Votre prénom doit faire au moins 2 caractères';

  if (!values.lastname) errors.lastname = 'Un nom est requis';
  else if (validator.isLength(values.lastname + '', {min: 0, max: 1})) errors.lastname = 'Votre nom doit faire au moins 2 caractères';

  if (!values.email) errors.email = 'Un email est requis';
  else if (!validator.isEmail(values.email + '')) {
    errors.email = 'Adresse email invalide';
  }

  if (!values.telephone) errors.telephone = 'Un telephone est requis';
  else if (!validator.isMobilePhone(values.telephone + '', 'fr-FR')) {
    errors.telephone = 'Téléphone invalide';
  }

  if (!values.password) errors.password = 'Un mot de passe est requis';
  else if (validator.isLength(values.password + '', {min: 0, max: 4})) errors.password = 'Votre mot de passe doit faire au moins 5 caractères';

  return errors;
};

export default reduxForm({
  form: 'profile',
  validate: validateProfileForm
})(ProfileForm);

Thanks a lot for your help, I feel like desperate (usual feeling for a noob I guess XD )


Solution

  • Your problem is simple, you don't bind anything to the onChange handler of select element. Generally, you can just take the onChange function provided as a prop by redux-form through the Field component.

    You can do something like this (removed bits of your code to just show the beef):

    export const renderSelectField = (field) => {
      // *snip snip*
      return (
        <div className={containerStyle}>
          {displayInputLabel(styles.idInput, field.label)}
          <select className='form-control' name={field.input.name} 
            id={styles.idInput} aria-describedby={styles.ariaDescribedBy} 
            onChange={field.input.onChange}>
            {field.options}
          </select>
          <span className={styles.glyphicon} aria-hidden='true' />
          {field.meta.touched && field.meta.error &&
        displayErrorMessage(field.meta.error)}
        </div>);
    };
    

    Hope this helps!