Forms: Components

Common foundational components used for form construction

Field

Component to...

Changes...

Doesn't reduce lines of code by much, by removes some boilerplate that could to easy copy/paste errors, and risk of typos leading to bad code (e.g. "errors['text'] vs field.error).

  • import Field from lore-react-forms
  • Remove touched from state, along with onBlur callback
  • Can reference field.error instead of

Before...

import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import _ from 'lodash';

export default createReactClass({
  displayName: 'FieldExample',

  getInitialState() {
    return {
      data: {
        text: ''
      },
      touched: {
        text: false
      }
    };
  },

  onChange(name, value) {
    const nextData = _.merge({}, this.state.data);
    nextData[name] = value;
    this.setState({
      data: nextData
    });
  },

  getValidators: function(data) {
    return {
      text: [function(value) {
        if (!value) {
          return 'This field is required';
        }
      }]
    };
  },

  getErrors: function(validatorDictionary, data) {
    return _.mapValues(data, function(value, key) {
      const validators = validatorDictionary[key];
      let error = null;
      if (validators) {
        validators.forEach(function(validator) {
          error = error || validator(value);
        });
      }
      return error;
    });
  },

  onBlur: function(field) {
    const touched = this.state.touched;
    touched[field] = true;
    this.setState({
      touched: touched
    });
  },

  render() {
    const { data, touched } = this.state;
    const validators = this.getValidators(data);
    const errors = this.getErrors(validators, data);

    return (
      <div className="modal-body">
        <div className="row">
          <div className="col-md-12">
            <div className={`form-group ${touched['text'] && errors['text'] ? 'has-error' : ''}`}>
              <label>
                Message
              </label>
              <textarea
                className="form-control"
                rows="3"
                value={data.text}
                placeholder="What's happening?"
                onChange={(event) => {
                  this.onChange('text', event.target.value)
                }}
                onBlur={() => {
                  this.onBlur('text');
                }}
              />
              {touched['text'] && errors['text'] ? (
                <span className="help-block">
                    {errors['text']}
                  </span>
              ) : null}
            </div>
          </div>
        </div>
      </div>
    );
  }

});

After...

import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import _ from 'lodash';
import { Field } from 'lore-react-forms';

export default createReactClass({
  displayName: 'FieldExample',

  getInitialState() {
    return {
      data: {
        text: ''
      }
    };
  },

  onChange(name, value) {
    const nextData = _.merge({}, this.state.data);
    nextData[name] = value;
    this.setState({
      data: nextData
    });
  },

  getValidators: function(data) {
    return {
      text: [function(value) {
        if (!value) {
          return 'This field is required';
        }
      }]
    };
  },

  getErrors: function(validatorDictionary, data) {
    return _.mapValues(data, function(value, key) {
      const validators = validatorDictionary[key];
      let error = null;
      if (validators) {
        validators.forEach(function(validator) {
          error = error || validator(value);
        });
      }
      return error;
    });
  },

  render() {
    const { data } = this.state;
    const validators = this.getValidators(data);
    const errors = this.getErrors(validators, data);

    return (
      <div className="modal-body">
        <div className="row">
          <div className="col-md-12">
            <Field name="text" data={data} errors={errors} onChange={this.onChange}>
              {(field) => {
                return (
                  <div className={`form-group ${field.touched && field.error ? 'has-error' : ''}`}>
                    <label>
                      Message
                    </label>
                    <textarea
                      className="form-control"
                      rows="3"
                      value={field.value}
                      placeholder="What's happening?"
                      onChange={(event) => {
                        field.onChange(event, event.target.value)
                      }}
                      onBlur={field.onBlur}
                    />
                    {field.touched && field.error ? (
                      <span className="help-block">
                        {field.error}
                      </span>
                    ) : null}
                  </div>
                )
              }}
            </Field>
          </div>
        </div>
      </div>
    );
  }

});