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

higherform

v1.0.1

Published

A ReactJS form library built around higher order components

Downloads

28

Readme

Higher Form

A ReactJS library for dealing with forms.

Quick Example

import React, { Component } form 'react';
import higherform, { fields, validators, FormShape, FieldsShape } from 'higherform';

class MyForm extends Component {
    static propTypes = {
        form: FormShape,
        fields: FieldsShape,
    }

    onSubmit(event) {
        event.preventDefault();

        // on form submit call the higher form submit method and provide a
        // handler for the form data.
        this.props.form.submit(formData => {
            // save formData somehow
        });
    }

    render() {
        let { form, fields} = this.props;

        return (
            <form onSubmit={this.onSubmit.bind(this)}>
                <input type="text" {...fields.name.props()} />
            </form>
        );
    }
}

const FieldSpec = {
    name: fields.input(validators.required('Please enter a name')),
}

export default higherform(FieldSpec)(MyForm);

Dynamic Field Specs from Props

Pass a function to higherform rather than a specification object. It will be invoked on object instantiation as well as whenever new props are received.

import React, { Component } form 'react';
import PropTypes from 'prop-types';
import higherform, { fields, validators, FormShape, FieldsShape } from 'higherform';

class MyForm extends Component {
    static propTypes = {
        form: FormShape,
        fields: FieldsShape,
        selectValues: PropTypes.arrayOf(PropTypes.string).isRequired,
    }

    onSubmit(event) {
        event.preventDefault();

        this.props.form.submit(formData => {
            // save formData somehow
        });
    }

    render() {
        let { form, fields, selectValues } = this.props;

        return (
            <form onSubmit={this.onSubmit.bind(this)}>
                <select {...fields.selectField.props()}>
                    <option value="">Select a Value</option>
                    {selectValues.map((v, key) => <option key={key} value={v}>{v}</option>)}
                </select>
            </form>
        );
    }
}

export default higherform(ownProps => {
    return {
        selectField: fields.select(validators.oneOf(ownProps.selectValues)),
    };
})(MyForm);

Working with Radio Buttons

The fields prop passed to your form component is an object of functions. Radio button fields require that you pass the value of the radio field. This helps higherform track whether or not the radio input should be checked.

import React, { Component } from 'react';
import higherform, { fields } from 'higherform';

class FormWithRadio extends Component {
    render() {
        return (
            let { fields } = this.props;
            <form>
                <input type="radio" {...fields.radioField.props('one')} />
                // render <input type="radio" value="one" onClick={...} checked={"one" === HigherForm.state.radioField} />
                <input type="radio" {...fields.radioField.props('two')} />
                <input type="radio" {...fields.radioField('three')} />
            </form>
        );
    }
}

export default higherform({
    radioField: fields.radio(),
})(FormWithRadio);

Setting Initial Values on the Form

higherform takes a second FormSpec object that deals with mapping the incoming (and next) props to form data. Form spec can provide propsToForm and nextPropsToForm methods.


import higherform, { fields } from 'higherform';

// same `MyForm component from above

function entityToForm(entity) {
    return {
        name: entity.name,
        otherField: entity.whatever,
    };
}

const FormSpec = {
    // invoke on component instiantiation. Use this to set the default values
    // in the form field. Should return an object with the field names as keys
    propsToForm: props => entityToForm(props.entity),

    // invoked whenever the component receives new props. This is useful for
    // updating the form if/when the underlying data changes. Say, for instance,
    // the form is to edit some entity in your application. If the underlying
    // entity prop change, the form should show the new values. Return an object
    // here to reset the form data. Or return a falsy value to decline doing
    // anything.
    nextPropsToForm: (props, nextProps) => {
        if (props.id !== nextProps.id) {
            return entityToForm(nextProps.entity);
        }
    },
};

const FieldSpec = {
    name: fields.input(),
    otherField: fields.select(),
};

export default higherform(FieldSpec, FormSpec)(MyForm);

Fields

Fields are barebones objects that adhere to the higherforms.fields.Field interface. Essentially the field abstraction is a way to define validation, what props are provied to your components, and how those form values should be stored.

There are several field types available.

import { fields } from 'higherform';

const FieldSpec = {
    // suitable for just about any <input /> tag except radio buttons and
    // checkboxes.
    input: fields.input(),

    // suitable for radio buttons and checkboxes.
    checkbox: fields.checkbox(),
    radio: fields.radio(),

    textarea: fields.textarea(),

    select: fields.select(),
};

Validators

Validators are functions that are invoke with the current form value as well as a validation context. A validation context provies a way to add violations (errors) to the form.

Here's a custom validator example.

import higherform, { fields } from 'higherform';

const FieldSpec = {
    // the function passed to `input` is a validator
    hello: fields.input(function (value, ctx) {
        if (! /hello/.test(value)) {
            ctx.addViolation('Name does not contain hello!');
        }
    }),
};

If any validators cause form violations, the form's handler (the callback passed to this.props.form.submit) is not invoked. Instead the form's state is updated and the violations are passed to your component at this.props.form.errors.

Built in Validators

import { fields, validators } from 'higherform';

const FieldSpec = {
    // marks the field as required. Only non-empty values will be accepted
    required: fields.input(validators.required('Error message to display if the field is empty')),

    // only allows the field to take a whitelist of values.
    whitelist: fields.input(validators.oneOf([
        'one',
        'two',
    ], 'error message to display to the user'),

    // only allows values that match the provied pattern.
    matches: fields.input(validators.matches(/hello/, 'error message to display')),

    // invokes all the validators provied.
    chain: fields.input(validators.chain(
        // both of these use the default error messages
        validators.required(),
        validators.oneOf(['one', 'two']),
    )),

    // like chain, this takes a set of validators, but it stops invoking them
    // if/when the first violation happens.
    shortChain: fields.input(validators.shortChain(
        validators.required(),
        validators.oneOf(['one', 'two']),
    )),

About Validator Messages

No built in validator requires a message. If you choose to provide a message it can be a string or a function. If a function, it will be invoked with the value.

const FieldSpec = {
    whitelist: fields.input(validators.oneOf([
        'one',
        'two',
    ], function (invalidValue) {
        return `${invalidValue} is not an acceptable answer.`;
    }),
};