This module is currently in alpha
This core library is in parallel development with a React
example repo which also contains a useFormMachine
hook.
The current create form api looks like this
import { form, fields } from 'xstate-form';
const machine = form({
id: 'myAwesomeForm',
fields: [
fields.text({ name: 'username' }),
fields.text({ name: 'password' }),
// ...
fields.submit({ name: 'submitForm' }),
],
initialValues: {
username: 'jaetask',
password: 'ThisIsTheWay',
},
});
The machine
const is now an xstate
compliant machine configuration object that can be passed to useMachine
or interpret
. The various form fields e.g. (form.text()
) return a state object with default event handling, actions and state transitions.
Here's an example of the text
state node.
simplified for berevity
{
id: name,
type: 'parallel',
states: {
focus: {
initial: 'unfocused',
states: {
focused: {
on: {
BLUR: {},
CHANGE: {},
FOCUS: {},
},
},
unfocused: {
on: {
FOCUS: {},
},
},
},
},
enable: {
// ...
},
visible: {
// ...
},
valid: {
// ...
},
meta: {
field: {
type: 'text',
},
},
},
});
note: we don't currently use the meta.field.type
but think this is a good idea for future use, form generation, testing etc
StateNode.id
, allows referencing in actionsValidation API currently in flux
Currently loking at a way to make all fields self validate and post status back to parent. Fields as Actors
Form validation works via a simple JS function, (this enables any validation library, including Yup
to be used by the user). There is a ticket to add Yup integration by default via validationSchema
import { form, fields } from 'xstate-form';
const machine = form({
id: 'myAwesomeForm',
validate: (values, event, meta, name) => {
const errors = {};
if (values.username.match(/[0-9]+/g)) {
errors.username = 'Username cannot include a number';
}
if (values.password.length <= 8) {
errors.password = 'Password must be > 8 chars';
}
return errors;
},
fields: [
fields.text({ name: 'username' }),
fields.text({ name: 'password' }),
// ...
fields.submit({ name: 'submitForm' }),
],
initialValues: {
username: 'jaetask',
password: 'ThisIsTheWay',
},
});
Validation works on every CHANGE
, FOCUS
and BLUR
event, this is similar to other form libraries, there will also be a VALIDATE
method to explicitly trigger validation yourself. see ticket
There are probably as many forms as programmers
The desire is to keep the API as clean as possible, so we will always use the 80/20 rule when deciding on features. If a request fits 80% of usecases then it will be considered.
The API is a set of composable functions to help you build an xstate
machine to handle forms. The idea is to take what you need and leave the rest, if you need an unsupported feature, feel free to copy/modify one of ours and roll your own for that specific scenario.
This approach should keep the API light, clean and easy to use.
This is my first typescript project, please ignore the any
params at the moment, especially during rapid prototyping, the API will get stricter over time, promise 😂.