Skip to content

Trioxis/immutable-react-form

Repository files navigation

immutable-react-form

Immutable form state management for ReactJS

Dependency Status devDependency Status

npm i --save @trioxis/immutable-react-form

Usage

Immutable form is a Higher Order Component (HOC), used like so:

import React from 'react';
import { LocalStateForm } from '@trioxis/immutable-react-form';
import { Map } from 'immutable';

function MyForm(props){
  const {
    form
  } = props;
  return <form onSubmit={form.submission.submit}>
    <input
      type='text'
      value={form.model.get('name')}
      onChange={(e)=>update(form.model.set('name',e.target.value))}
    />
  </form>
}

LocalStateForm(
  // A function which takes props, and returns the form's model
  props => ({name: ''}),

  // The validation function. See Validation below for details
  //   This is a simple default
  () => new Map({}),

  // The submission function. Should return a promise
  (model, props) => Promise.resolve()
)(
  MyForm
)

Full props are as follows:

const {
  form:{
    model, // Immutable JS
    update, // (model)=>void (expects entire model)
    meta:{
      dirty // bool (whether or not the model has changed)
    },
    validation:{
      get, // (keyPath)=>{valid:bool,message:string>}
      isValid, //bool
    },
    submission:{
      submit, // (event)=>void
      loading, // bool
      lastSubmission: {
        success, // bool
        error // error
      }
    }
  }
} = props;

Validation

Validation function must return an (Immutable JS) Map where each key is a path to the validation object.

If a path is not defined, it is assumed valid.

Example:

function validateForm(model){
  return new Map({
    ...(model.get('name').split(' ').length !== 2)
      && { name: { valid:false, message:'Apparently you should only have 2 names' } },
      ...!model.get('name')
      && { name: { valid:false, message:'Name is required' } },
    ...(model.get('experience').size < 3)
      && { experience : { valid:false, message:'Please write 3 or more things' } },
    ...(
      model.get('experience')
      .map((exp,i)=>new Map({item:exp,index:i}))
      .filter(exp=>!(['Education','Job'].includes(exp.getIn(['item','type']))))
      .reduce((acc,curr,i)=>({
        ...acc,
        [`experience.${curr.get('index')}.type`]: { valid:false, message:'Can be "Education" or "Job"'}
      }),{})
    )
  })
}

Recipies

Smart submit button

(props)=>
  <input
    type='submit'
    value={props.form.submission.loading ? 'Submitting...' : 'Submit'}
    disabled={(
      !props.form.meta.dirty ||
      !props.form.validation.isValid ||
      props.form.submission.loading
    )}
  />

Releases

No releases published

Packages

No packages published