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

formtools

v0.1.0

Published

Higher order components for React to manage forms and fields

Downloads

2

Readme

formtools

npm install formtools --save

Higher order components (HoC) for React to manage forms and fields.

Overview

These components are usefull for handling simple or medium-complex forms, and when you don't want to use tools like redux to manage the state of your fields.

The 'Field' HoC adds common behaviour to a form field, mainly normalization, validation, and keeping the field 'controlled'. The goal here is to keep your field completely stateless.

The 'Form' HoC acts as a state container with some extra functionalities. These functionalities are passed down as props.

These components are very unopinionated on how to inplement them in your app. They also don't depend on each other, which make them suitable for test.

Features

  • Keeps your fields presentational
  • Small footprint
  • Highly plexible and customizable
  • State is managed for you
  • Can be used with other tools easily

Basic usage

Field

// Field example A: just a controlled input

import React from 'react';
import { Field } from 'formtools';

function Input({ onChange, value }) {
    return (
        <input type="text" value={value} onChange={onChange} />
    );
}

export default Field({
    valueMap: event => event.target.value
})(Input);
// Field example B: more complex

import React from 'react';
import { 
    Field, 
    expect, 
    targetValue, 
    defaultFieldProps 
} from 'formtools';

function Input({ error, success, label, setRef, ...props }) {
    // determine the className for the input element
    let cx;
    if (error) cx = 'error';
    if (success) cx = 'success';

    return (
        <div className="field">
            <label htmlFor={props.name}>{label}</label>
            <input
                ref={setRef} // set ref, i.e. to call focus() in Form component
                className={cx}
                {...defaultFieldProps(props)} // helper which adds value, events, etc.
                type="text"
            />
            {error && <div className="errorMessage">{error}</div>}
        </div>
    );
}

export default Field({
    valueMap: targetValue, // helper: event => event.target.value
    normalize: val => val.toUpperCase(),
    validate: [
        // access child field properties within validation rules
        expect((val, _, field) => field('name').value.length > 0, 'Please fill in your name first'),
        expect(val => val.length >= 3, 'This value is to short'),
        expect(val => val.length < 9, 'This value is to long')
    ]
})(Input);
// Form example:

import React, { Component } from 'react';
import { Form } from 'formtools';

import Zipcode from './Zipcode';
import checkZipcode from './api';

@Form()
class RegistrationForm extends Component {

    handleSubmit = () => {
        // function from HoC
        const { validate, getValues, asyncError } = this.props;

        // validate every field and display error / successs if possible
        // return true of every field passes validation
        if (!validate()) return;

        const values = getValues();
        
        // example: check with api if zipcode is valid
        checkZipcode(values.zipcode)
            .then(data => {
                // on error manually overwrite error message of Zipcode field
                if (!data) asyncError('zipcode', 'Invalid zipcode!');
                // do something with all the field values :)
                else someAction(values)
            });
    }

    render() {
        return (
            <div>
                <Zipcode name="zipcode">
                ... other fields

                <button onClick={this.handleSubmit}>Submit</button>
            </div>
        )
    }
}

Docs

Field(config: Object): (Component): Component

Typical decorator pattern. Usage:

// es6
const Enhanced = Field({})(Component);

// es7
@Field({})

config

All of the config settings are totally optional.

  • valueMap

    A function which describes where to find the value in a onChange-handler. Typical usecase is 'event => event.target.value'.

  • normalize

    A function which takes a value and transforms it into another value. I.e.:

    val => val.toUpperCase()
  • validation

    Can be a function or an array of functions. Must be used together with to expect function. I.e:

    expect((value, ownProps, otherField) => value.length > 0, 'Error message on failure')

    expect calls your validator function with 3 arguments:

    • value: the value after normalization
    • ownProps: specific properties your form gave this field
    • field: a function which can be called with a field-name and returns an object containing value, error, success, dirty and touched

    In case of multiple validators, the validation will stop on the first error, so it is wise to sort your validators by priority.

Injected props

These props will be passed to the wrapped component:

  • Standard form events: onChange, onBlur, onFocus, onKeyDown, onKeyUp, onKeyPress,
  • value
  • name
  • error: string if there's an validation error, boolean (false) if not
  • success: boolean
  • setRef: allows HoC to access the inputs dom-node (optional)
  • Other props you've given..

Props

  • name required
  • Standard form events like onChange. All these event have a signature of (value, name)
  • Special event onValidChange, which will be called as soon as a value passes all validation
  • Own custom props, which will be passed down to the original wrapped component
  • initValue: value to start with, defaults to ''
  • aggressive: boolean which validates after each onChange instead of after the blur event

Validation behaviour

  • if no validators are set, success and error both will stay false

  • Validation will occur:

    • after the onBlur event
    • after the onChange event when the prop aggressive is set
    • after the validate method of the Form HoC is used

Form

Typical decorator pattern. Usage:

// es6
const Enhanced = Form()(Component);

// es7
@Form()

Exposed methods via props:

getValues()

Returns an object with fieldnames as key and their corresponding values.

validate(x?: string | string[], soft: boolean = false)

Validates all fields by default, unless a fieldname or an array of fieldnames is given, and returns true if all fields passed validation, false otherwise. Soft mode is disabled by default. When set to true, validation happens silently in the background without rendering error/success

clear(x?: string | string[])

Resets all fields by default, unless a fieldname or an array of fieldnames is given. All values of the field will be '' (empty), and the props success, error, touched and dirty will be set to false.

reset(x?: string | string[])

Same as clear(), except that reset will set the value back to initValue if possible

asyncError(fieldname: string, errorMessage: string)

Manually overwrite an error message of a field after a async call.

asyncErrorClear(fieldname: string)

Clears to above error and sets the validation before the async-error was set.

node(fieldname: string)

Returns a dom-node of a field. See setRef prop in Field.

event(type: string, handler: (value: any, name: string))

Allows you to subscribe to common events. Usage:

this.props.events(
    'onChange',
    (value, name) => console.log(`The value of ${name} is '${value}'`)
);

field(fieldname: string)

Return an object with data:

  • value
  • success
  • error
  • touched: field was at least focussed once
  • dirty: field was changed at least once