npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-informal

v0.1.4

Published

React forms without the ceremony

Downloads

13

Readme

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", "[email protected]", and "+44 200 200 200" for name, email, and tel respectively, the submit function would be called with {name: "John", email: "[email protected]", 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.