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 )
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!