In this short post, I'm going to walk you through how I'm handling HTML forms with Mobx.js. I've developed my own little library (~2.4Kb) for handling HTML forms with Mobx, which makes the forms fully reactive. It supports fields that depend on other fields and asynchronous validation.
You can checkout the demo that shows pretty much all the functionality of the library or check out the source (library and demo)on github
Usage
To use the library we need to create the schema
object, which declares what fields exist and what validation tests to use for every field.
In the example below we are going to create a schema with only one field (email
) and that field will have only one validation (there could be an array of validations), that will check if the field value is a valid email address.
import { createField, createValidation } from 'dumba'
import isEmail from 'validator/lib/isEmail'
const schema = {
email: createField({
value: 'admin@example.com',
validations: createValidation(
(str: string) => isEmail(str),
'Not a valid email'
)
})
}
After we create the schema, we use the Form
class to create the class instance that will be connected to the actual HTML form. Form instance accepts the schema.
import { Form } from 'dumba'
import { schema } from './schema'
const form = new Form(schema)
When the form instance is created, we use it to connect the fields to the actual HTML form. The form will have all the fields from the schema e.g.
const form = new Form(schema)
form.fields.email.value // field value
form.fields.email.onChange //field change event handler to connect to html input
form.fields.email.errors // array of validation errors (if any)
// also there are properties and methods on the form instance itself
form.isValid // boolean if the form is valid
form.isSubmitting// boolean if form is in the process of submitting
form.isValidating // boolean if the form is in the process of validating (async validations)
form.handleSubmit // submit the form function
Now, let's connect the form to the Material UI TextField as an example
//using material ui just as an example
import TextField from '@material-ui/core/TextField'
import { observer } from 'mobx-react-lite'
import { Form } from 'dumba'
import { schema } from './schema'
//declare it outside of the component, usual Mobx rules apply.
const form = new Form(schema)
const FormDemo = observer(function FormDemo() {
//form submit function
const handleOnSubmit = () =>
form.handleSubmit((form: Form) => Promise.resolve(true))
return (
<form onSubmit={handleOnSubmit} autoComplete="off" noValidate>
<TextField
type="text"
id="email"
name="email"
label="Email"
// disable while submit is in progress or async validation is running
disabled={form.isSubmitting || form.isValidating}
value={form.fields.email.value}
onChange={form.fields.email.onChange}
onBlur={form.fields.email.onChange}
// mark field as invalid (if there are any email errors)
error={!!form.fields.email.errors.length}
helperText={<DisplayErrors errors={form.fields.email.errors} />} // display error text
autoComplete="off"
/>
<Button //form submit button
variant="contained"
color="primary"
type="submit"
disabled={
// disable button while submit or async validation is in progress or form is invalid or not yet validated
form.isSubmitting ||
form.isValidating ||
!form.isValid ||
!form.isValidated
}
>
Submit
</Button>
</form>
)
})
And that's the gist of it. There is a lot more you can do with the library. For example, you can create fields that depend on other fields.
In the next example, we are going to create two schema fields location
and numPeople
. The latter will depend on the former which means that every time the location
field value changes, a validation test for numPeople
will also run.
const schema: SchemaType = {
location: createField({
value: 'beach' // or pool
}),
numPeople: createField({
value: '',
dependsOn:['location'] // depends on the location field above
validations: [
createValidation(
(partyPeople: number, _field: Field, locationDependancy?: Field) => {
if (locationDependancy?.value === 'pool') {
if (partyPeople > 20) {
return 'max pool party attendance is 40' // error message
}
return true //valid
}
if (locationDependancy?.value === 'beach') {
if (partyPeople > 200) {
return 'max beach party attendance is 200' // error message
}
return true //valid
}
}
)
]
})
}
This little library has sped up my form handling workflow considerably, so I've decided to open source it.
I hope you find it useful.
Top comments (0)