Reasonably making forms sound good again (pun 100% intended)
yarn add bs-reform
Then add it to bsconfig.json
"bs-dependencies": [
"bs-reform"
]
Dealing with forms directly can scalate really quickly to a hell when not using the right approach. We created ReForm to be both deadly simple and to make forms sound good leveraging ReasonML's powerful typesytem. Even the schemas we use are nothing more than constructors built-in in the language itself with a small size footprint.
Checkout demo/src/app.re
also
module SignUpFormParams = {
type state = {
password: string,
email: string
};
type fields = [ | `password | `email];
/* (fieldName, getter, setter) */
let lens = [
(`email, (s) => s.email, (s, email) => { ...s, email }),
(`password, (s) => s.password, (s, password) => { ...s, password }),
];
};
module SignUpForm = ReForm.Create(SignUpFormParams);
let component = ReasonReact.statelessComponent("SignUp");
let make = (~signInMutation, _children) => {
...component,
render: (_) => {
<SignUpForm
initialState={password: "", email: ""}
schema=[
(`password, Required),
(`email, Email),
]
onSubmit=((values, ~setError, ~setSubmitting) => whatever(values, ~setError, ~setSubmitting))
>
...(
({
form,
handleChange,
handleSubmit,
getErrorForField
}) =>
<FormWrapper>
<ErrorWarn error=form.error/>
<FieldsWrapper>
<FormField
fieldType=FormField.TextField
value=form.values.email
placeholder="Email"
style=fieldsStyle
placeholderTextColor=AppTheme.Colors.blackLight
onChangeText=handleChange(`email)
/>
<ErrorText value=getErrorForField(`password)/>
<FormField
fieldType=FormField.TextField
placeholder="Password"
onChangeText=handleChange(`password)
value=form.values.password
style=fieldsStyle
placeholderTextColor=AppTheme.Colors.blackLight
/>
etc
</FieldsWrapper>
<RaisedButton text="Sign in" onPress=handleSubmit/>
</FormWrapper>
)
</SignUpForm>
}
}
We tried to make the API simple yet very powerful, so you don't have to worry about learning a lot of quirks
When you create a new ReForm module you get a brand new ReasonReact component
/* Just some regular ReasonReact guy */
module Form = ReForm.Create(SignUpFormParams);
These are the props/params it accepts:
The schema tells to ReForm how to validate your date, take a look at Schema to see more
We let this scape hatch for when the provided validators aren't enough for you and you need some more complexity.
let validate: SignUpForm.values => option(string) = (values) => {
switch (values) {
| { email: "[email protected]" } when values.password === "secretThing" => Some("Can't do.")
| _ => None
}
}
<Form
validate
/* Yes! You can still use schema with it */
schema=[(`email, Email)]
>
The returned valued of validate
will set reform.form.error
This is the guy you'll be putting your POST/mutation/whatever logic into, it is triggered after handleSubmit
is called.
let onSubmit = (values, ~setError, ~setSubmitting) => {
Js.Promise.(
values
|> convertToJS
|> mutate
|> then_(response => {
switch(response##error |> Js.Null_undefined.to_opt) {
| None =>
setSubmitting(false);
doSomeOtherThing();
| Some(error) =>
setSubmitting(false);
setError(Some("Something went wrong, try again"))
}
})
)
|> ignore
}
<Form schema onSubmit>
You can pass a custom dictionary to be shown as the validators errors
The param passed to the children is a record of the following type
type reform = {
form: state,
handleChange: (Config.fields, value) => unit,
handleGlobalValidation: option(string) => unit,
handleSubmit: unit => unit,
getErrorForField: Config.fields => option(string)
};
Accessed via reform.form
and contains the following
{
/* The record containing the actual form state */
values: Params.state,
/* The submitting state */
isSubmitting: bool,
/* This is intended to store global validation error, like a submitting failure */
error: option(string)
}
handleChange
takes the field in question and its string value, we made like this so you can use it both in Web and React Native
Triggers the submitting and makes ReForm set reform.form.isSubmitting
to true
Returns the validation error, if there is any, for the field in question
Handles the global error value at reform.form.error
The schema used by ReForm is nothing more than a tuple and the validator is a constructor thus the final representation is really lightweight and does not enforce you to bring Yet Another Schema Validator For JS.
It is passed as a param to the form, <SignInForm schema>
ReForm accepts a validation schema that looks like
(fieldName, getter, validator)
or
(`email, s => s.email, Email)
Take a look in the demo to see it in action.
(`password, s => s.password, Custom(values => values.password == "123" ? Some("Really?") : None))
(`fullName, s => s.fullName, Required)
(`email, s => s.email, Email)
If you have any doubts don't hesitate to reach out the wonderful https://discord.gg/reasonml