2016-11-16 16:58:25 +00:00
|
|
|
import React, { Component, PropTypes } from 'react';
|
2016-12-21 17:25:54 +00:00
|
|
|
import { isEqual, noop } from 'lodash';
|
2016-11-16 16:58:25 +00:00
|
|
|
|
2016-11-21 15:43:08 +00:00
|
|
|
const defaultValidate = () => { return { valid: true, errors: {} }; };
|
|
|
|
|
|
|
|
export default (WrappedComponent, { fields, validate = defaultValidate }) => {
|
2016-11-16 16:58:25 +00:00
|
|
|
class Form extends Component {
|
|
|
|
static propTypes = {
|
|
|
|
errors: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
|
|
|
formData: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
|
|
|
handleSubmit: PropTypes.func,
|
2016-11-21 15:43:08 +00:00
|
|
|
onChangeFunc: PropTypes.func,
|
2016-11-16 16:58:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static defaultProps = {
|
|
|
|
errors: {},
|
|
|
|
formData: {},
|
|
|
|
};
|
|
|
|
|
|
|
|
constructor (props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
const { errors, formData } = props;
|
|
|
|
|
2016-12-23 18:40:16 +00:00
|
|
|
if (!errors) {
|
|
|
|
this.state = { errors: {}, formData };
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-11-16 16:58:25 +00:00
|
|
|
this.state = { errors, formData };
|
2016-12-23 18:40:16 +00:00
|
|
|
|
|
|
|
return false;
|
2016-11-16 16:58:25 +00:00
|
|
|
}
|
|
|
|
|
2016-12-21 17:25:54 +00:00
|
|
|
componentWillReceiveProps (nextProps) {
|
|
|
|
const { formData: formDataProp } = nextProps;
|
|
|
|
const { formData: oldFormDataProp } = this.props;
|
|
|
|
|
|
|
|
if (!isEqual(formDataProp, oldFormDataProp)) {
|
|
|
|
const { formData } = this.state;
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
formData: {
|
|
|
|
...formData,
|
|
|
|
...formDataProp,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-11-16 16:58:25 +00:00
|
|
|
onFieldChange = (fieldName) => {
|
|
|
|
return (value) => {
|
|
|
|
const { errors, formData } = this.state;
|
2016-11-21 15:43:08 +00:00
|
|
|
const { onChangeFunc = noop } = this.props;
|
|
|
|
|
|
|
|
onChangeFunc(fieldName, value);
|
2016-11-16 16:58:25 +00:00
|
|
|
|
|
|
|
this.setState({
|
|
|
|
errors: { ...errors, [fieldName]: null },
|
|
|
|
formData: { ...formData, [fieldName]: value },
|
|
|
|
});
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
onSubmit = (evt) => {
|
|
|
|
evt.preventDefault();
|
|
|
|
|
|
|
|
const { handleSubmit } = this.props;
|
|
|
|
const { errors, formData } = this.state;
|
|
|
|
const { valid, errors: clientErrors } = validate(formData);
|
|
|
|
|
|
|
|
if (valid) {
|
|
|
|
return handleSubmit(formData);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
errors: { ...errors, ...clientErrors },
|
|
|
|
});
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
getError = (fieldName) => {
|
|
|
|
const { errors } = this.state;
|
|
|
|
const { errors: serverErrors } = this.props;
|
|
|
|
|
2016-12-23 18:40:16 +00:00
|
|
|
if (serverErrors) {
|
|
|
|
return errors[fieldName] || serverErrors[fieldName];
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors[fieldName];
|
2016-11-16 16:58:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getFields = () => {
|
|
|
|
const { getError, getValue, onFieldChange } = this;
|
|
|
|
const fieldProps = {};
|
|
|
|
|
|
|
|
fields.forEach((field) => {
|
|
|
|
fieldProps[field] = {
|
|
|
|
error: getError(field),
|
|
|
|
name: field,
|
|
|
|
onChange: onFieldChange(field),
|
|
|
|
value: getValue(field),
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
return fieldProps;
|
|
|
|
}
|
|
|
|
|
|
|
|
getValue = (fieldName) => {
|
|
|
|
return this.state.formData[fieldName];
|
|
|
|
}
|
|
|
|
|
|
|
|
render () {
|
|
|
|
const { getFields, onSubmit, props } = this;
|
|
|
|
|
|
|
|
return <WrappedComponent {...props} fields={getFields()} handleSubmit={onSubmit} />;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Form;
|
|
|
|
};
|