0.1.4 • Published 7 years ago

react-informal v0.1.4

Weekly downloads
5
License
MIT
Repository
github
Last release
7 years ago

React Informal

React forms without the ceremony.

Use components — the primary form of abstraction in React — to configure forms. No up-front setup, no state-management boilerplate.

Rules, like field names or validations, are supplied as props to field components, which become registered by to form simply by mounting. This approach leads to modular, reusable, and flexible code.

If you find this pattern familiar, it is because this is exactly how the regular HTML form elements work! :sparkles:

Installation

# Using yarn
yarn add react-informal

# Using npm
npm install --save react-informal

Examples

Barebones

Suppose we have the following tiny app:

import React from "react";
import ReactDOM from "react-dom";
import { Form, connectField } from "react-informal";

// Barebones text field component
const TextField = connectField()(({ field, ...rest }) => (
  <input {...rest} {...field.input} />
));

// Mock submission function
const submit = formData => {
  alert(JSON.stringify(formData));
  return Promise.resolve();
};

// Main view component
const View = () => (
  <Form onSubmit={submit}>
    <TextField name="name" />
    <TextField name="email" />
    <TextField name="tel" />
    <button type="submit">Submit</button>
  </Form>
);

// Render components to DOM
ReactDOM.render(<View/>, document.getElementById("app"));

Now, if the user fills in the above form with "John", "john@example.com", and "+44 200 200 200" for name, email, and tel respectively, the submit function would be called with {name: "John", email: "john@example.com", tel: "+44 200 200 200"} as arguments once the Submit button is clicked.

import React from "react";
import { Form, connectField, connectForm } from "react-informal";

// Form sub-components
const mapFieldProps = field => ({...field.input, messages: field.messages });
const TextField = connectField(mapFieldProps)(({ label, messages, ...rest }) => (
  <div>
    <div>{label}</div>
    <input {...rest} />
    {messages.length > 0 &&
      messages.map(message => <div key={message}>{message}</div>)}
  </div>
));

const mapHandlerProps = form => ({ error: form.error });
const ErrorHandler = connectForm(mapHandlerProps)(({ error }) => (
  <div>
    {error && <p>Form submission failed</p>}
  </div>
));

const mapSubmitProps = form => ({ valid: form.valid });
const SubmitButton = connectForm(mapSubmitProps)(({ valid }) => (
  <button type="submit" disabled={!valid}>Submit</button>
));

// Mock submission function
const submit = formData =>
  new Promise(resolve => setTimeout(resolve, 350, formData));

// Validation objects
const required = { test: /(.+)/, message: "This field is required" };
const email = { test: /(.+)@(.+)\.(.+)/, message: "Email has to be valid" };

// Main view component
const View = () => (
  <Form onSubmit={submit}>
    <TextField label="Name" name="name" validations={[required]} />
    <TextField label="Email" name="email" validations={[required, email]} />
    <TextField label="Telephone" name="tel" />
    <ErrorHandler />
    <SubmitButton />
  </Form>
);

API Overview

Form Component

The Form component provides top-level form state, exposes it to nested components and handles submissions.

This component has one required prop: onSubmit.

onSubmit should be passed a function that returns a Promise. This function will be called with form data object (a map of field names to field values) when a valid form is submitted.

All other props will be passed to the underlying DOM element <form>.

Once onSubmit is triggered, the following things will happen:

  1. The form and all nested fields will be marked as submitted. Any connected component can use this prop to, for instance, show validation messages.
  2. If the form is valid, the function passed to onSubmit will be called and form status prop set to "pending". If the form is not valid, no further actions will happen.
  3. If the promise returned from the submit function fulfills, the form status will be set to fulfilled, and the fulfilled value will be made available under the data prop.
  4. If the promise returned from the submit function rejects, the form status will be set to rejected, and the rejected value will be made available under the error prop.
// Example usage

const View = () => (
  <Form onSubmit={data}>
    {/* children */}
  </Form>
)

connectField Higher-Order Component

The connectField registers a form field element in the top-level form state upon mounting, and supplies relevant state and methods to the component so that it can update the state.

This is the component you will probably interact with the most.

connectForm Higher-Order Component

The connectForm subscribes to the entire form state and supplies it as props to the wrapped component.

Tips & Tricks

Use the prop mapping functions in connectForm and connectField higher-order components to make sure the API of your wrapped components is nice and clean, so that they can be easily used both with and without being enhanced.

For example:

import React from "react";
import { connectField } from "react-informal";

const TextField = ({label, visited, messages, ...rest}) => (
  <div>
    <label>{label}</label>
    <input {...rest} />
    {visited &&
      messages.map(message => <div key={message}>{message}</div>)}
  </div>
);

const mapProps = field => ({
  ...field.input,
  visited: field.visited,
  messages: field.messages
})

export { TextField }
export default connectField(mapProps)(TextField)

Now the default export is a "connected" component that expects to be used within the context of a <Form>, but the secondary export can just as well be used standalone in cases where <Form> might not be needed.

0.1.4

7 years ago

0.1.3

7 years ago

0.1.2

7 years ago

0.1.1

7 years ago

0.1.0

7 years ago

0.1.0-1

7 years ago

0.1.0-0

7 years ago

0.0.0

7 years ago