react-mobforms
TypeScript icon, indicating that this package has built-in type declarations

1.0.1 • Public • Published

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 of
  • this.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 10
  • this.fieldValue is equivalent to this.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 fields
    • this.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 in formState
  • type -- One of String, 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

Readme

Keywords

Package Sidebar

Install

npm i react-mobforms

Weekly Downloads

2

Version

1.0.1

License

ISC

Unpacked Size

31 kB

Total Files

20

Last publish

Collaborators

  • thegrtman