This package is published as ES Modules, so please use a bundler such as Webpack.
- Installation
- Basic usage
- Supported field types
- Validation
- Magical type refinement
- APIs
- Customizing error messages
- Using your own validators
- License
npm install -S react-magical-formyarn add react-magical-formimport 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>
);
};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" />;
};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" />;
};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" />;
};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.
*/}
</>;
);
};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.
*/}
</>;
);
};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" />;
};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 />;
};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;
};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;
};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;
};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;
};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;
};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;
};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;
};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;
};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;
};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;
};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;
};Alias to exact(), but its default error message is different.
This is supporsed to used in useRules().
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;
};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.
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[]]
})}
/>
);
};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
})}
/>
);
};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
})}
/>
);
};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
})}
/>
);
};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"
})}
/>
);
};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')} />;
};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;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;
};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;
};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;
};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;
};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;
};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;
};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;
};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;
};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(() => {})} />;
};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;
};[WIP]
[WIP]
MIT