react-magical-form v0.0.5
react-magical-form
Attention
This package is published as ES Modules, so please use a bundler such as Webpack.
Table of contents
- Installation
- Basic usage
- Supported field types
- Validation
- Magical type refinement
- APIs
- Customizing error messages
- Using your own validators
- License
Installation
npm install -S react-magical-formyarn add react-magical-formBasic 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
Using your own validators
License
MIT