Skip to content

Instantly share code, notes, and snippets.

@jsdir
Last active February 1, 2016 04:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jsdir/b35311b4619057bfb356 to your computer and use it in GitHub Desktop.
Save jsdir/b35311b4619057bfb356 to your computer and use it in GitHub Desktop.

react-fields

react-fields includes utilities for easily building forms from a data schema.

Installation

npm install --save react-fields

Quickstart and Examples

The following examples illustrate some of the different features and utilities of react-fields.

A simple login form

react-fields provides sensible defaults for quickly creating forms. To demonstrate, we'll create a simple login form using renderForm:

import React from 'react';
import { render } from 'react-dom';

import { renderForm } from 'react-fields';

const schema = {
  fullName: {
    type: 'string',
    rules: {
      required: true
    }
  },
  age: {
    type: 'number',
    rules: {
      required: true
    }
  },
  password: {
    type: 'string',
    rules: {
      required: true,
      min: 8
    }
  }
};

const SignupForm = () => {
  const form = renderForm(schema, {
    submit: values => console.log('Submitted:', values)
  }, fields => (
    <div>
      <fieldset>
        <h4>Personal Info</h4>
        <label>Full Name</label>
        {fields.render('fullName')}
        <label>Age</label>
        {fields.render('age')}
      </fieldset>
      <label>Password</label>
      {fields.render('password')}
    </div>
  ))

  return (
    <div>
      <h2>Signup</h2>
      {form}
    </div>
  )
};

render(<SignupForm/>, document.body);

renderForm automatically infers what type of input component to render from the field type. Also, notice the stateless component SignupForm. Since the form manages it's own state, we can easily render forms inside both stateful and stateless components. When the form is submitted, the submit callback is called with the submitted field values if the validation succeeds.

The rules defined in schema are used for client-side validation. When invalid data is submitted, the fields will automatically show the errors and submit will not be called.

Custom field components

react-fields renders default components for different field types, but you may want to customize this. You can change the default field components to any controlled component with value and onChange props. We can do this in three levels:

  1. Override the field component by type using fieldComponents.
renderForm(schema, {
  fieldComponents: {
    string: SuperTextBox,
    number: MyNumberBox
  }
}, fields => (
  // ...
));
  1. Override the field component by field using fieldComponent.
const schema = {
  fullName: {
    type: 'string',
    fieldComponent: SuperTextBox
  }
};
  1. Override the field component using fields.render.
renderFields(schema, fields => (
  <div className="form-horizontal">
    {fields.render('fullName', SuperTextBox)}
    {fields.render('age', MyNumberBox)}
    {fields.render('password')}
  </div>
));

TODO: Document custom field component props

Creating a form renderer

We don't want to specify the same options each time we render a form. react-fields exports a createFormRenderer helper that you can use. Here's an example of how this can be used within the organization of a larger app.

We define custom field components in their own directory.

// fieldComponents/SuperTextBox.jsx

export default const SuperTextBox = (props) => (
  <input className="is-super" {...props}/>
);

Create our own custom renderer:

// utils.js

import { createFormRenderer } from 'react-fields';

import SuperTextBox from './fieldComponents/SuperTextBox';

export const renderForm = createFormRenderer({
  fieldComponents: {
    string: SuperTextBox
  },
  submitButton: onClick => (
    <button onClick={onClick}>Custom Submit</button>
  )
});

Now we can import the custom renderer instead of the vanilla one from react-fields:

import { renderForm } from './utils'

const SignupForm = () => {
  const form = renderForm(schema, {
    // ...
  }, fields => (
    // ...
  ))
};

Client validation

You can add custom validators to your fields with custom. Asynchronous validation is supported as well.

import api from 'api'

async isRegisteredPhoneNumber(value, fieldName, attrs) {
  const isValid = await api.isRegisteredPhoneNumber(value)
  return isValid ? true : 'This phone number is invalid'
}

const schema = {
  phone: {
    type: 'string',
    rules: {
      match: /^(\([0-9]{3}\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}$/,
      custom: [isRegisteredPhoneNumber]
    }
  }
};

TODO: document createValidator

Server validation

Sometimes the server will validate the data and it might return validation errors. react-fields expects there to be two kinds of server-side validation errors, form-level and field-level. Both of these cases can be handled with submit. submit can be an async function, resolving with an error payload if submitting the data was not successful.

const saveUser = async (data) => {
  // For example, this action will always return an error.
  return {
    summaryError: 'Something went wrong in the API',
    fieldErrors: {
      fullName: 'Not a real name',
      age: 'Invalid age'
    }
  }
}

renderForm(schema, {
  submit: saveAccount,
  afterSubmit: data => console.log(data, 'was valid and was submitted successfully')
}, fields => (
  <div>
    {fields.render('fullName')}
    {fields.render('age')}
  </div>
))

react-fields provides an afterSubmit callback that is called when submit resolves without an error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment