react-mobforms
v1.0.1
Published
Automatically create mobx stores from forms
Readme
react-mobforms
Installation
npm install react-mobformsNote 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.fieldValuewill be a number with a starting value of 10this.fieldValueis 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.formStatewill 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 informStatetype-- 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
