FormContainer

Notes

A wrapping component for managing field state and form submission. FormContainer creates no UI, but passes it's props on to its child, which is typically a form element. It receives a fields configuration prop, and decorates it as fieldsState. Each field gains dynamic error, wasChanged, and wasValid properties. A nested input object maintains properties for the field's associated input. i.e. id, name, required, value, onChange, onBlur. Every change event in the child form triggers a re-render with updated field state (including validation).

Props

  • childrennode[<div />]
  • fields
    shape {
    requiredbool
    validatorfunc
    inputobject
    }
    [{}]

    An object to configure fields using input names as the key.

    Validation can specified for each field in the following ways:

    • required: true (default) uses the validateExistence as validator
    • required: false no validation
    • validator A custom validator function
  • focusOnSubmitErrorbool

    Set focus on the first invalid input during submittals.

  • onErrorfunc[() => {}]

    Callback for invalid form submittals. Invoked with validation results for invalid fields.

  • onFieldsStateChangefunc[() => {}]

    Callback for when the field state has changed. Invoked with fieldsState, oldFieldsState

  • onFieldUpdateFromEventfunc[() => {}]

    Callback for a when field has been updated from a dom event. Invoked with the event, fieldState.

  • onSubmitfunc[() => {}]

    Callback for valid form submittals. Invoked with form data.

  • focusOnInvalidAfterAnimationbool

    Forces focusOnFirstInvalid to wait for animations so that focus can be properly applied.

const ExampleFields = ({ fields, isValid }) => {
/**
*
* Example of a field state object:
* {
* error: true, // true when previously valid or the input is blurred
* wasBlurred: true,
* wasChanged: true,
* wasValid: true,
* input: {
* id: "fieldOne",
* name: "fieldOne",
* onBlur: handleBlur,
* onChange: handleChange,
* required: true,
* value: ""
* }
* }
*/
// console.log('fieldState objects:', fields);
const handleClick = () => alert(`This form ${isValid ? 'is' : 'is not' } in a valid state.`)
return (
<React.Fragment>
<Field
{...fields.fieldOne.input}
error={shouldDisplayError(fields.fieldOne)}
errorMessage='Please enter a value'
className='mb2'>
fieldOne
</Field>
<Field
{...fields.fieldTwo.input}
className='mb2'>
fieldTwo (optional)
</Field>
<Field
{...fields.fieldThree.input}
error={shouldDisplayError(fields.fieldThree)}
errorMessage='Please enter digits only'
className='mb2'>
fieldThree (only digits)
</Field>
<Button type='submit' className='mt2' onClick={handleClick}>Submit</Button>
</React.Fragment>
)
};
class FormContainerExample extends React.Component {
constructor() {
super();
this.fields = {
fieldOne: {},
fieldTwo: {
required: false
},
fieldThree: {
validator: ({ value }) => {
return { error: value === '' || /\D/.test(value) }
}
}
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleError = this.handleError.bind(this);
}
handleSubmit(formData) {
// All fields are valid, do something with the formData.
// console.log('handleSubmit', formData);
}
handleError(invalidFields) {
// Some fields are not valid.
//console.log('handleError', invalidFields);
}
render() {
return (
<FormContainer
focusOnSubmitError
fields={this.fields}
onSubmit={this.handleSubmit}
onError={this.handleError}
>
<Form requiredIndicatorTextClassName="text-bold">
<ExampleFields />
</Form>
</FormContainer>
);
}
}
render(<FormContainerExample />);

Form

Notes

An opinionated form element that is designed to work the FormContainer component. When used with FormContainer as its parent, Form will pass fields and isValid through to its children.

If any fields are required, a message will be added to the top of the form indicating the requirement to the user.

Props

  • childrennode*
  • fields
    shape {
    requiredbool
    validatorfunc
    inputobject
    }
    [{}]

    An object to configure fields using input names as the key.

    If wrapped in a FormContainer, these will be passed down automatically and do not need to be defined.

  • isValidbool

    Boolean representing whether all of the form's fields pass clientside validation or not

    If using a FormContainer as the parent, FormContainer provides this value.

  • requiredIndicatorTextstring['indicates required field']

    The text that appears above the form, that describes the meaning of the required indicator on fields. If the fields prop is passed, the message will be shown if any fields are marked as required. If fields is not passed, the message will always be shown.

  • requiredIndicatorTextClassNamestring['']

    Use this prop to customize the styling for the required indicator message.

  • method['post']
  • noValidate[true]
const ExampleForm = ({ fields }) => {
return (
<React.Fragment>
<Field
{...fields.fieldOne.input}
error={shouldDisplayError(fields.fieldOne)}
errorMessage='Please enter a value'
className='mb2'
id="fieldOneId"
>
fieldOne
</Field>
<Button type='submit' className='mt2'>Submit</Button>
</React.Fragment>
)
};
class FormExample extends React.Component {
constructor() {
super();
this.fields = {
fieldOne: {
input: { required: true }
},
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleError = this.handleError.bind(this);
}
handleSubmit(formData) {
// All fields are valid, do something with the formData.
// console.log('handleSubmit', formData);
}
handleError(invalidFields) {
// Some fields are not valid.
//console.log('handleError', invalidFields);
}
render() {
return (
<Form
fields={this.fields}
onSubmit={this.handleSubmit}
onError={this.handleError}
requiredIndicatorTextClassName="text-bold">
<ExampleForm />
</Form>
);
}
}
render(<FormExample />);