import React, { Component, PropTypes } from 'react'; import { isEqual, noop } from 'lodash'; const defaultValidate = () => { return { valid: true, errors: {} }; }; export default (WrappedComponent, { fields, validate = defaultValidate }) => { 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, onChangeFunc: PropTypes.func, }; static defaultProps = { errors: {}, formData: {}, }; constructor (props) { super(props); const { errors, formData } = props; if (!errors) { this.state = { errors: {}, formData }; return false; } this.state = { errors, formData }; return false; } 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; } onFieldChange = (fieldName) => { return (value) => { const { errors, formData } = this.state; const { onChangeFunc = noop } = this.props; onChangeFunc(fieldName, value); 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; if (serverErrors) { return errors[fieldName] || serverErrors[fieldName]; } return errors[fieldName]; } 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 ; } } return Form; };