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-magical-form

v0.0.5

Published

Form builder for React

Downloads

4

Readme

react-magical-form

Attention

This package is published as ES Modules, so please use a bundler such as Webpack.

Table of contents

Installation

npm install -S react-magical-form
yarn add react-magical-form

Basic Usage

import React from 'react';
import { checkbox, number, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { field, handleSubmit } = useForm({
    name: text(),
    age: number(),
    acceptsPolicy: checkbox(),
  });

  return (
    <form
      onSubmit={handleSubmit((values) => {
        // Do something
      })}
    >
      <div>
        <input ref={field('name')} type="text" />
      </div>

      <div>
        <input ref={field('age')} type="number" />
      </div>

      <div>
        <input ref={field('acceptsPolicy')} type="checkbox" />
      </div>

      <button type="submit">Submit</button>
    </form>
  );
};

Supported field types

text()

Results in string.

import React from 'react';
import { text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { field, getValues } = useForm({
    foo: text({ initial: '' }),
  });

  getValues().foo; //=> string

  return <input ref={field('foo')} type="text" />;
};

number()

Results in number | undefined.

import React from 'react';
import { number, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { field, getValues } = useForm({
    foo: number({ initial: undefined }),
  });

  getValues().foo; //=> number | undefined

  return <input ref={field('foo')} type="number" />;
};

checkbox()

Results in boolean.

import React from 'react';
import { checkbox, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { field, getValues } = useForm({
    foo: checkbox({ initial: false }),
  });

  getValues().foo; //=> boolean

  return <input ref={field('foo')} type="checkbox" />;
};

textChoice()

Results in string | undefined.

import React from 'react';
import { textChoice, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { field, getValues } = useForm({
    foo: textChoice({ initial: undefined }),
  });

  getValues().foo; //=> string | undefined

  return (
    <>
      <input ref={field('foo')} type="radio" name="foo" value="bar" />
      {/*                       ^^^^^^^^^^^^
                                `type` property is required and it should be "radio".
      */}
      <input ref={field('foo')} type="radio" name="foo" value="baz" />
      {/*                                    ^^^^^^^^^^
                                             `name` property is required
                                               with the same value of field name.
      */}
    </>;
  );
};

numberChoice()

Results in number | undefined.

import React from 'react';
import { numberChoice, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { field, getValues } = useForm({
    foo: numberChoice({ initial: undefined }),
  });

  getValues().foo; //=> number | undefined

  return (
    <>
      <input ref={field('foo')} type="radio" name="foo" value="0" />
      {/*                       ^^^^^^^^^^^^
                                `type` property is required and it should be "radio".
      */}
      <input ref={field('foo')} type="radio" name="foo" value="1" />
      {/*                                    ^^^^^^^^^^
                                             `name` property is required
                                               with the same value of field name.
      */}
    </>;
  );
};

file()

Results in File | undefined.

import React from 'react';
import { file, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { field, getValues } = useForm({
    foo: file(),
  });

  getValues().foo; //=> File | undefined

  return <input ref={field('foo')} type="file" />;
};

files()

Results in readonly File[].

import React from 'react';
import { files, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { field, getValues } = useForm({
    foo: files(),
  });

  getValues().foo; //=> readonly File[]

  return <input ref={field('foo')} type="file" multiple />;
};

Validation

Field spec

required()

import React from 'react';
import {
  checkbox,
  file,
  files,
  number,
  numberChoice,
  required,
  text,
  textChoice,
  useForm,
} from 'react-magical-form';

export const Form: React.FC = () => {
  useForm({
    f1: text({
      spec: required(), // should not be empty
    }),
    f2: textChoice({
      spec: required(), // should be selected
    }),
    f3: number({
      spec: required(), // should be empty and a number value
    }),
    f4: numberChoice({
      spec: required(), // should be selected
    }),
    f5: checkbox({
      spec: required(), // should be checked
    }),
    f6: file({
      spec: required(), // should be uploaded
    }),
    f7: files({
      spec: required(), // should be uploaded at least one
    }),
  });

  return null;
};

minLength(limit: number)

import React from 'react';
import { minLength, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  useForm({
    f1: text({
      spec: minLength(10), // should have length which is greater than or equal to 10
    }),
  });

  return null;
};

maxLength(limit: number)

import React from 'react';
import { maxLength, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  useForm({
    f1: text({
      spec: maxLength(32), // should have length which is smaller than or equal to 32
    }),
  });

  return null;
};

pattern(regexp: RegExp)

import React from 'react';
import { pattern, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  useForm({
    f1: text({
      spec: pattern(/-foo-/), // should match given pattern
    }),
  });

  return null;
};

oneOfTexts(...values: readonly string[])

import React from 'react';
import { oneOfTexts, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  useForm({
    f1: text({
      spec: oneOfTexts('foo', 'bar', 'baz'), // should be one of 'foo', 'bar', and 'baz'
    }),
  });

  return null;
};

min(limit: number)

import React from 'react';
import { min, number, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  useForm({
    f1: number({
      spec: min(1), // should be greater than or equal to 1
    }),
  });

  return null;
};

max(limit: number)

import React from 'react';
import { max, number, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  useForm({
    f1: number({
      spec: max(10), // should be smaller than or equal to 10
    }),
  });

  return null;
};

range(min: number, max: number)

import React from 'react';
import { number, range, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  useForm({
    f1: number({
      spec: range(0, 10), // should be between 0 and 10
    }),
  });

  return null;
};

integer()

import React from 'react';
import { integer, number, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  useForm({
    f1: number({
      spec: integer, // should be an integer
    }),
  });

  return null;
};

oneOfNumbers(...values: readonly number[])

import React from 'react';
import { oneOfNumbers, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  useForm({
    f1: text({
      spec: oneOfNumbers(1, 2, 3), // should be one of 1, 2, and 3
    }),
  });

  return null;
};

exact<T extends number | string>(value: T)

import React from 'react';
import { exact, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  useForm({
    f1: text({
      spec: exact('foo'), // should be 'foo'
    }),
  });

  return null;
};

confirmationOf<T extends number | string>(value: T)

Alias to exact(), but its default error message is different.
This is supporsed to used in useRules().

Form rules

If some fields have rules depend on other fields, use useRules().

import React from 'react';
import {
  confirmationOf,
  required,
  text,
  useForm,
  validationError,
} from 'react-magical-form';

export const Form: React.FC = () => {
  const { useRules } = useForm({
    password: text({
      spec: required(),
    }),
    passwordConfirmation: text({
      spec: required(),
    }),
  });

  // If you use `useRules()`, DO NOT USE any apis from `useForm()` except `useRules()`.
  // Instead, use apis from `useRules()`.
  const { errors, field, getValues, ...apis } = useRules({
    passwordConfirmation: (otherValues) => confirmationOf(otherValues.password),
  });

  return null;
};

Magical type refinement

If handleSubmit runs its callback, all validations should be passed, so some types of field values can be refined.
react-magical-form implements this behavior.

Refinement by required()

import React from 'react';
import {
  checkbox,
  file,
  files,
  number,
  numberChoice,
  required,
  textChoice,
  useForm,
} from 'react-magical-form';

export const Form: React.FC = () => {
  const { getValues, handleSubmit } = useForm({
    f1: textChoice({
      spec: required(),
    }),
    f2: number({
      spec: required(),
    }),
    f3: numberChoice({
      spec: required(),
    }),
    f4: checkbox({
      spec: required(),
    }),
    f5: file({
      spec: required(),
    }),
    f6: files({
      spec: required(),
    }),
  });

  getValues().f1; //=> string | undefined
  getValues().f2; //=> number | undefined
  getValues().f3; //=> number | undefined
  getValues().f4; //=> boolean
  getValues().f5; //=> File | undefined
  getValues().f6; //=> readonly File[]

  return (
    <form
      onSubmit={handleSubmit((values) => {
        values.f1; //=> string
        values.f2; //=> number
        values.f3; //=> number
        values.f4; //=> true
        values.f5; //=> File
        values.f6; //=> readonly [File, ...File[]]
      })}
    />
  );
};

Refinement by oneOfTexts()

import React from 'react';
import {
  number,
  numberChoice,
  oneOfTexts,
  text,
  textChoice,
  useForm,
} from 'react-magical-form';

export const Form: React.FC = () => {
  const { getValues, handleSubmit } = useForm({
    f1: textChoice({
      spec: oneOfTexts('foo', 'bar'),
    }),
  });

  getValues().f1; //=> string | undefined

  return (
    <form
      onSubmit={handleSubmit((values) => {
        values.f1; //=> "foo" | "bar" | undefined
      })}
    />
  );
};

Refinement by oneOfNumbers()

import React from 'react';
import {
  number,
  numberChoice,
  oneOfNumbers,
  text,
  textChoice,
  useForm,
} from 'react-magical-form';

export const Form: React.FC = () => {
  const { getValues, handleSubmit } = useForm({
    f1: number({
      spec: oneOfNumbers(1, 2),
    }),
    f2: numberChoice({
      spec: oneOfNumbers(1, 2),
    }),
  });

  getValues().f1; //=> number | undefined
  getValues().f2; //=> number | undefined

  return (
    <form
      onSubmit={handleSubmit((values) => {
        values.f1; //=> 1 | 2 | undefined
        values.f2; //=> 1 | 2 | undefined
      })}
    />
  );
};

Refinement composition

import React from 'react';
import {
  compose,
  number,
  numberChoice,
  oneOfNumbers,
  oneOfTexts,
  required,
  textChoice,
  useForm,
} from 'react-magical-form';

export const Form: React.FC = () => {
  const { getValues, handleSubmit } = useForm({
    f1: textChoice({
      spec: oneOfTexts('foo', 'bar'),
    }),
    f2: textChoice({
      spec: compose(
        required(),
        oneOfTexts('foo', 'bar'),
      ),
    }),
    f3: number({
      spec: oneOfNumbers(1, 2),
    }),
    f4: numberChoice({
      spec: compose(
        required(),
        oneOfNumbers(1, 2),
      ),
    }),
  });

  getValues().f1; //=> string | undefined
  getValues().f2; //=> string | undefined
  getValues().f3; //=> number | undefined
  getValues().f4; //=> number | undefined

  return (
    <form
      onSubmit={handleSubmit((values) => {
        values.f1; //=> "foo" | "bar" | undefined
        values.f2; //=> "foo" | "bar"
        values.f3; //=> 1 | 2 | undefined
        values.f4; //=> 1 | 2
      })}
    />
  );
};

Refinement by useRules()

import React from 'react';
import {
  compose,
  confirmationOf,
  oneOfTexts,
  required,
  text,
  textChoice,
  useForm,
} from 'react-magical-form';

export const Form: React.FC = () => {
  const { useRules } = useForm({
    field: textChoice({
      spec: compose(
        required(),
        oneOfTexts('foo', 'bar'),
      ),
    }),
    fieldConfirmation: text(),
  });

  const { getValues, handleSubmit } = useRules({
    fieldConfirmation: (otherFields) => confirmationOf(otherFields.field),
  });

  getValues().field; //=> string | string
  getValues().fieldConfirmation; //=> string

  return (
    <form
      onSubmit={handleSubmit((values) => {
        values.field; //=> "foo" | "bar"
        values.fieldConfirmation; //=> "foo" | "bar"
      })}
    />
  );
};

APIs

field(fieldName: <NAME_OF_FIELD>)

Bind HTML element to field instance.

import React from 'react';
import { text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { field } = useForm({
    f1: text(),
  });

  return <input ref={field('f1')} />;
};

useRules(rules: <FORM_RULES>)

Set form rules and return new form APIs.

const { useRules } = useForm({
  beginning: number({
    spec: required(),
  }),
  end: numberChoice({
    spec: required(),
  }),
});

const { errors, field, getValues, ...apis } = useRules({
  end: (otherValues) => min(otherValues.beginning),
});

return null;

getValues()

import React from 'react';
import { number, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { getValues } = useForm({
    f1: text(),
    f2: number(),
  });

  getValues(); //=> { "f1": string, "f2": number | undefined }

  return null;
};

setValue(fieldName: <NAME_OF_FIELD>, value: <VALUE_TYPE_OF_FIELD>)

import React from 'react';
import { number, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { setValue } = useForm({
    f1: number(),
  });

  setValue('f1', 0);
  setValue('f1', undefined);

  return null;
};

setValues(values: <VALUES_TYPE>)

import React from 'react';
import { number, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { setValues } = useForm({
    f1: text(),
    f2: number(),
  });

  setValues({
    f1: 'foo',
    f2: 1,
  });

  return null;
};

errors

Error messages of latest validation, as object with same keys of fields.

import React from 'react';
import { required, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { errors } = useForm({
    f1: text({
      spec: required(),
    }),
  });

  errors; //=> { "f1": readonly string[] }

  return null;
};

validate()

Runs validations manually.

import React, { useEffect } from 'react';
import { required, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { validate } = useForm({
    f1: text({
      spec: required(),
    }),
  });

  useEffect(() => {
    validate();
  }, []);

  return null;
};

reset()

Sets all field values to initial values and clears all errors.

import React, { useEffect } from 'react';
import { required, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { reset } = useForm({
    f1: text({
      spec: required(),
    }),
  });

  useEffect(() => {
    reset();
  }, []);

  return null;
};

clear()

Clears all field values and errors.

import React, { useEffect } from 'react';
import { required, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { clear } = useForm({
    f1: text({
      spec: required(),
    }),
  });

  useEffect(() => {
    clear();
  }, []);

  return null;
};

clearErrors()

Clears all errors.

import React, { useEffect } from 'react';
import { required, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { clearErrors } = useForm({
    f1: text({
      spec: required(),
    }),
  });

  useEffect(() => {
    clearErrors();
  }, []);

  return null;
};

handleSubmit(f: (values: <VALUES_TYPE>) => void | Promise<void>)

Handles form submittion running given callback.

import React, { useEffect } from 'react';
import { required, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { handleSubmit } = useForm({
    f1: text({
      spec: required(),
    }),
  });

  return <form onSubmit={handleSubmit(() => {})} />;
};

isSubmitting

import React from 'react';
import { required, text, useForm } from 'react-magical-form';

export const Form: React.FC = () => {
  const { isSubmitting } = useForm({
    f1: text({
      spec: required(),
    }),
  });

  isSubmitting; //=> boolean

  return null;
};

Customizing error messages

[WIP]

Using your own validators

[WIP]

License

MIT