@clearc2/c2-form v3.2.1
@clearc2/c2-form 
Flexible forms that use ImmutableJS and can be integrated with redux.
Install
yarn add @clearc2/c2-formUsage
The easiest way to get started is using the useForm hook.
import React from 'react'
import {useForm} from '@clearc2/c2-form'
function NewsletterSignUpForm () {
const form = useForm({name: '', email: ''}) // pass initial values
const values = form.currentValues
return (
<form>
<label>Name</label>
<input
type='text'
value={values.get('name')}
onChange={(e) => form.setValue('name', e.target.value)}
/>
<label>Email</label>
<input
type='text'
value={values.get('email')}
onChange={(e) => form.setValue('email', e.target.value)}
/>
<button type='submit' disabled={!form.isDirty}>
Submit
</button>
</form>
)
}See props below to see all properties of form
Redux support
To use the redux HOC, formHOC, add the reducer to your main application's reducer using the required key.
import {combineReducers} from 'redux-immutable'
import {reducer as formReducer} from '@clearc2/c2-form'
const reducer = combineReducers({
// ...other reducers
[formReducer.key]: formReducer
})Redux usage
import {connect} from 'react-redux'
import {formHOC} from '@clearc2/c2-form'
import SiteForm from './SiteForm'
const props = {siteId} => ({formName: `site-form-${siteId}`})
export default connect(props)(formHOC(SiteForm))The formHOC gives your component props and connected actions scoped to this specific form based on the formName.
Redux-less HOC
There is also a redux-less HOC that uses component state. You do not need to pass in a formName.
The passed down props are exactly the same except for deleteForm. This is not passed down because deleteForm is only relevant
for the redux enabled formHOC HOC.
import {withForm} from '@clearc2/c2-form'
import SiteForm from './SiteForm'
export default withForm(SiteForm)Props
initialValues: object
The initial values of the form. Defaults to Map().
currentValues: object
The current values of the form. Defaults to Map().
isDirty: boolean
true if the initialValues do not equal the currentValues. Defaults to false.
isClean: boolean
true if the initialValues equal the currentValues. Defaults to true.
setInitialValues(initialValues: object, currentValues: ?object): func
Initialize your form in the componentDidMount lifecycle method with this action and pass in an object of field/value pairs.
If a second argument is passed, it will set the current values equal to that argument. Needing to send currentValues should be very rare.
setValue(field: string, value: any): func
Set a value in the form. Use this in an input's onChange callback.
setValues(values: object): func
Set multiple values in the form at once. The values object should be field/value pairs.
deleteField(field: string): func
Delete a form field.
deleteFields(fields: array): func
Delete multiple form fields.
reset(): func
Sets the currentValues back to the initialValues.
deleteForm(): func
Deletes the formName key and data from redux.
useValues<T>()
This is a version of useForm that does not convert the underlying data to immutable-js. It also supports a type generic so type safety is enforced.
Tips
Default values
Always ensure a non-null default in your inputs. React will complain about the input changing from an uncontrolled to a controlled input if the value goes from null to something non-null while having an onChange defined.
<input
value={this.props.currentValues.get('foobar') || ''}
onChange={e => this.props.setValue('foobar', e.target.value)}
/>Updating child objects
It is a common requirement to be able to update child objects. This can be done with setValue.
const lineItems = this.props.currentValues.get('lineItems') || List()
{lineItems.map((lineItem, i) => (
<div key={i}>
<input
value={lineItem.get('name') || ''}
onChange={e => {
const updatedItems = lineItems.update(i, item => item.set('name', e.target.value))
this.props.setValue('lineItems', updatedItems)
}}
/>
</div>
))}