Skip to content

Instantly share code, notes, and snippets.

@kurtisdunn
Last active August 26, 2018 04:12
Show Gist options
  • Save kurtisdunn/0218198f0bfb8b222fb4109d866770fd to your computer and use it in GitHub Desktop.
Save kurtisdunn/0218198f0bfb8b222fb4109d866770fd to your computer and use it in GitHub Desktop.
React Form Component

React Form Component

Forms need to be able to pass state to deeply nested children, specifically validation state. You will need map React.Children in order to setState for each child. Unfortunately this doesn't solve the problem of of deeply nested inputs. In order to combat this we will need to use a recursive function to check against each nested level to see if it has an input for validation.

  function recursiveCloneChildren(children) {
    const that = this;
    return React.Children.map(children, child => {
      var childProps = {};
      if (React.isValidElement(child)) {
        childProps = { errors: that.state.errors };
      }
      if (child.props) {
        childProps.children = this.recursiveCloneChildren(child.props.children);
        return React.cloneElement(child, childProps);
      }
      return child;
    });
  }
import React from 'react';
import assign from 'object-assign';
import emit from '../../utils/emit';
import Alert from '../../components/alert';
import validate from '../../utils/validator/form';
const $ = window.$;
function emitResponse (elem, cmpnt) {
return function (response) {
cmpnt.setState({ response: response });
};
}
function emitSuccess (elem, cmpnt) {
return function (response) {
cmpnt.setState({ response: cmpnt.state.response, responseType: 'success' });
};
}
function emitError (elem, cmpnt) {
return function (response) {
cmpnt.setState({ response: cmpnt.state.response, responseType: 'danger' });
};
}
function serialize(elem) {
elem.querySelector(('button[type="submit"]')).setAttribute('disabled', 'true');
return $(elem).serializeArray().reduce(function(prev, curr) {
var selects = [].slice.call(elem.querySelectorAll('select'));
let selectArray;
if (selects.length > 0) {
selectArray = selects.reduce(function(prev, curr) {
if (prev.hasOwnProperty(curr.id)) {
if (Array.isArray(prev[curr.id])) {
prev[curr.id].push(curr.value);
} else {
prev[curr.id] = [prev[curr.name], curr.value];
}
} else {
prev[curr.id] = curr.value;
}
return prev;
}, {});
}
if (prev.hasOwnProperty(curr.name)) {
if (Array.isArray(prev[curr.name])) {
prev[curr.name].push(curr.value);
}
} else {
prev[curr.name] = curr.value;
}
const result = assign({}, prev, selectArray);
return result;
}, {});
}
export default class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
erros: null
};
this.handleSubmit = this.handleSubmit.bind(this);
this.recursiveCloneChildren = this.recursiveCloneChildren.bind(this);
}
handleSubmit(event) {
const that = this;
const form = event.target;
const handler = this.props.method;
form.querySelector(('button[type="submit"]')).removeAttribute('disabled');
if (handler) {
event.preventDefault();
}
validate(event.target, function (field, errors) {
}, function (form, errors) {
that.setState({
errors: errors
});
if (!handler || errors.length) {
return;
}
handler(serialize(form))
.then(emitResponse(form, that))
.then(emitSuccess(form, that))
.catch(emitError(form, that));
});
}
recursiveCloneChildren(children) {
const that = this;
return React.Children.map(children, child => {
var childProps = {};
if (React.isValidElement(child)) {
childProps = { errors: that.state.errors };
}
if (child.props) {
childProps.children = this.recursiveCloneChildren(child.props.children);
return React.cloneElement(child, childProps);
}
return child;
});
}
render() {
return (
<div>
{ this.state.response ? <Alert type={ this.state.responseType } message={ this.state.response }/> : null}
<form onSubmit={this.handleSubmit} >
{ this.recursiveCloneChildren(this.props.children) }
</form>
</div>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment