react-mobforms
v1.0.1
Published
Automatically create mobx stores from forms
Downloads
1
Readme
react-mobforms
Installation
npm install react-mobforms
Note that react-mobforms
has mobx
and mobx-react
as required peer dependencies
Usage
Form field decorator
Form fields come in a few flavors:
String
@formField('myField', String, '') export default class MyField extends FieldComponent {
Number
@formField('myField', Number, 0) export default class MyField extends FieldComponent {
String array
@formField('myField', [String], ['']) export default class MyField extends FieldComponent {
Number array
@formField('myField', [Number], [0]) export default class MyField extends FieldComponent {
Functional usage
formField
can also be used as a composable function, as such:
class MyField extends FieldComponent {
/* your implementation */
}
export default formField('name', String, '')(MyField)
Effect of the formField decorator
The formField
decorator exposes two fields to the wrapped component.
this.formState
-- The state of the entire form that this field is a member ofthis.fieldValue
-- The value of the field defined by the decorator call
For example: If the decorator was used as @formField('myField', Number, 10)
this.fieldValue
will be a number with a starting value of 10this.fieldValue
is equivalent tothis.formState.myField
Basic examples
If not provided, the type and default value are String
and ''
. For instance, in the following example the field on formState
will be called firstName
, be a string, and start with a value of ''
.
import * as React from 'react'
import { formField, FieldComponent } from 'react-mobforms'
@formField('firstName')
export default class FirstName extends FieldComponent {
render () {
return (
<input
placeholder='First Name'
value={this.fieldValue}
onChange={ev => this.fieldValue = ev.currentTarget.value}
/>
)
}
}
You can also set a default value for the field. In this example, the field on formState
will be called lastName
, and will also be a string, but will instead start with a value of 'Doe'
.
import * as React from 'react'
import { formField, FieldComponent } from 'react-mobforms'
@formField('lastName', String, 'Doe')
export default class LastName extends FieldComponent {
render () {
return (
<input
placeholder='Last Name'
value={this.fieldValue}
onChange={ev => this.fieldValue = ev.currentTarget.value}
/>
)
}
}
In the following example, the field on formState
is called age
, is a number, and starts with a value of 18
.
import * as React from 'react'
import { formField, FieldComponent } from 'react-mobforms'
@formField('age', Number, 18)
export default class Age extends FieldComponent {
render () {
return (
<input
placeholder='Age'
value={`${this.fieldValue || ''}`}
onChange={ev => this.fieldValue = parseInt(ev.currentTarget.value, 10)}
/>
)
}
}
Form decorator
The form decorator can be used as such:
@form
export default class Form extends FormComponent {
It can also be used as a composable function:
class MyForm extends FormComponent {
/* your implementation */
}
export default form(MyForm)
Effect of the form decorator
The form
decorator exposes a single field to the wrapped component.
this.formState
-- The state of the the form and all of its fieldsthis.formState
will have all of the fields defined by its children (and their children)
In the following example, let's assume that FirstName
, LastName
, and Age
are defined as above:
import * as React from 'react'
import { form, FormComponent } from 'react-mobforms'
import FirstName from './FirstName'
import LastName from './LastName'
import Age from './Age'
@form
export default class UserForm extends FormComponent {
render () {
return (
<form onSubmit={onSubmit}>
<FirstName />
<LastName />
<Age />
<button type='submit'>Submit</button>
</form>
)
}
onSubmit = () => {
console.log(this.formState)
}
}
In this example, formState
will have firstName
, lastName
, and age
as fields, with starting values of ''
, 'Doe'
, and 18
, respectively. Whenever any of these fields is updated, the change is automatically propagated down to all of the children that consume the property.
Replacing formState
Sometimes, you will want your form to contain information that is not strictly the defaults for each of the fields. Let's say you already have a user
with firstName
, lastName
, and age
. If you want to replace what is in the existing formState
, you simply write this.formState = user
. Note that only the fields defined by the form's fields will be copied to this.formState
. In the prior example, if you were to write this.formState = { foo: 'bar' }
, this would set firstname
, lastName
, and age
to undefined
, but would not copy the foo
property, because it is not a field defined by the form.
In practical terms, you can expose a prop from your form component which allows you to replace the defaults. For example:
@form
export default class UserForm extends FormComponent {
componentDidMount () {
this.formState = this.props.defaultState
}
/* rest of implementation */
}
In this way, if rendered as <UserForm defaultState={user} />
, then the fields from user
would replace the defaults for all of the form fields whose field name matches one from user
.
Hidden fields
For fields that don't directly appear in your form, you can use the HiddenField
component. This component has three props:
name
-- The name of the field informState
type
-- One ofString
,Number
,[String]
, or[Number]
defaultValue
-- The default value for the field
When using the HiddenField
component, all three of these props are required.
You can use this to drive other components using the hidden field. For example:
@formField('primaryLanguage', String, 'English')
class PrimaryLanguage extends FieldComponent {
render () {
const { languages } = this.formState
return languages.map(language => (
<label key={language}>
<input
type='radio'
name='primaryLanguage'
value={language}
onChange={this.onChange}
/>
{language}
</label>
))
}
@action onChange = (ev) => {
this.fieldValue = ev.currentTarget.value
}
}
@form
export default class MyForm extends FormComponent {
render () {
return (
<form>
<HiddenField name='languages' type={[String]} defaultValue={['English', 'Deutsch', 'Français', 'Español']} />
<PrimaryLanguage />
</form>
)
}
}
Additional examples
For more examples, see the examples directory.
Planned features
- Fields can be marked as non-enumerable, thus making them not appear in
JSON.stringify(formState)
- Forms can also be fields in parent forms, allowing for nested form structure
- Field-level validation
- Custom field types