codemask-labs/forms
codemask-labs/forms
is a continuation of old react-form-builder-v2
package. Currenctly in alpha due to breaking changes and testing.
New features:
- less code
- less re-renders
- zod schemas for validation
- uses stan-js underneath
Basic usage
- Start with schema definition. It can be placed inline or in separate file (as a hook):
import z from 'zod'
const useOTPFormSchema = () => {
const phoneNumber = z
.string()
.length(9, {
message: 'Phone number must have 9 digits'
})
.default('')
const otpCode = z
.string()
.optional()
.default('123456')
return {
phoneNumber,
otpCode
}
}
const App = () => {
const { form, submit, isValid } = useForm({
fields: useOTPFormSchema(),
onSuccess: ({ phoneNumber, otpCode }) => {
},
onError: errors => {
}
})
return (
<form
onSubmit={event => {
event.preventDefault()
submit()
}}
>
<Input field={form.name} />
<Input field={form.email} />
<button disabled={!isValid}>
Submit
</button>
</form>
)
}
- Pass
field
to your form components:
const Input: React.FunctionComponent<{ field: ReactiveFormField<string> }> = ({ field: useField }) => {
const { value, onChange, reset, error, validate } = useField()
return (
<div>
<input
value={value}
onChange={event => onChange(event.target.value)}
onBlur={() => validate()}
/>
<span>{error}</span>
<button
type='button'
onClick={reset}
>
Reset
</button>
</div>
)
}
[!WARNING]
If you use React Compiler, make sure you rename field
to useField
- otherwise entire hook will be memoized and won't update the state
API
useForm
form | Record<string, ReactiveFormField<any>> | all fields that should be passed to corresponding components |
submit | () => void | Submit the form |
reset | (...args?: Array<string>) => void | resets either entire form or specific fields |
getValues | () => Record<string, any> | Returns your form values as object |
isValid | boolean | Defines if your form is valid |
updateForm | (fields: Partial<Record<string, Partial<FormField<any>>>) => void | stanjs method to update form state |
useFormEffect | (fields: Record<string, any>) => void | stanjs effect - function that allows to subscribe to store's values change |
useForm config
fields | Record<string, ZodDefault<any>> | zod based schema for your form |
onSuccess | (fields: Record<string, any>) => void | called when your form is valid |
onError | (errors: Record<string, string>) => void | called when form is not valid, returns a pair for field key and error message |
onSubmit | (fields: Record<string, any>) => void | called after the validation, but before onSuccess or onError, useful for additonal validation like dependent fields |
field (ReactiveFormField<any>>)
error | string | field error message passed from zod |
hasError | boolean | defines if your field has error |
value | any | current field value |
onChange | (value: any) => void | function to update current field value |
reset | () => void | resets current field to defaults |
validate | () => void | should be called to validate the field eg. onBlur event |
Notes
If you want to dynamically set field as optional you can either conditionally return different schemas, or create yourself simple util to do this:
export const dynamicOptional = <TSchema extends z.ZodDefault<z.ZodTypeAny>>(schema: TSchema, optional?: boolean) =>
optional ? schema.optional().default(schema._def.defaultValue()) : schema