WrapInput

To make your inputs ready for React Reform you need to wrap them first. The WrapInput component should provide all functionality to interface with all kinds of components.

Let's see a simple example to get an idea of what it looks like

import {WrapInput} from 'react-reform'

const Checkbox = props => (
  <WrapInput type="Checkbox" directProps={props}>{
    ({value, themeProps: {onChange, ...restThemeProps}}) => (
      <input type="checkbox" checked={value || false}
        onChange={e => onChange(e.target.checked)}
        {...restThemeProps}
      />
    )
  }</WrapInput>
)

Okay, so what is going on here?

Checkbox is a functional component that returns a WrapInput component with a child-as-function. Here you put your raw input component and tell it how to interface with React Reform's primitives.

Most importantly you tell how the value is set on your component and how to extract the value from its onChange callback.

WrapInput props

Let's take a more detailed look at how to use the WrapInput component.

directProps

This should always be the same: whatever props the user puts on the wrapped component should be passed as directProps. The theme's renderField function will then decide which of these props will be propagated to the real component via themeProps.

focusFn (optional)

Defines how the component's reference can be used to focus the input when there's a validation error (and we don't skip this behaviour via setting dontFocusAfterFail). The default definition looks like this: function() {if (!this.shouldPreventFocusAfterFail || !this.shouldPreventFocusAfterFail()) node.focus()}.

children: (value, themeProps) => (React Element)

As seen in the example above, WrapInput' child needs to be a function. This function provides two arguments, value and themeProps to allow adjusting to the raw input component's needs.

  • value describes the value of the form's model. This value might need to be translated such that the input component can understand it.
  • themeProps contains all the props that are passed to the input compoent via the theme. In addition it'll contain onChange, onFocus and onBlur listeners that you might have to adapt in order to get the correct behaviour. It also contains a ref callback to get a handle on the rendered instance to allow focusing it when validation fails.

Examples

Let's take a look at various DatePickers to see how we can wrap them.

react-datepicker

import React from 'react'
import {Form, ReformContext, WrapInput} from 'react-reform'
import defaultValidations from 'react-reform/opt/validations'
import defaultTheme from '../default-theme'
import RawDatePicker from 'react-datepicker'
import 'react-datepicker/dist/react-datepicker.css'

const DatePicker = props => (
  <WrapInput type="DatePicker" directProps={props} focusFn={node => node.setOpen.call(node, true)}>{
    ({value, themeProps}) => (
      <RawDatePicker selected={value} {...themeProps}/>
    )
  }</WrapInput>
)

export default class ExampleForm extends React.Component {

  handleSubmit = (data) => {
    console.log('data', data)
  }

  render() {
    return (
      <ReformContext themes={{default: defaultTheme}} validations={defaultValidations}>
        <div>
          <h4>Form</h4>
          <Form onSubmit={this.handleSubmit}>
            <DatePicker name="startDate" isRequired/>
          </Form>
        </div>
      </ReformContext>
    )
  }
}

See it in action

Form

is required
Check your console to see the submitted value

react-date-picker

import React from 'react'
import {Form, ReformContext, WrapInput} from 'react-reform'
import defaultValidations from 'react-reform/opt/validations'
import defaultTheme from '../default-theme'
import {DateField} from 'react-date-picker'
import 'react-date-picker/index.css'

const DatePicker = props => (
  <WrapInput type="DatePicker" directProps={props}>{
    ({value, themeProps}) => (
      <DateField value={value || null} {...themeProps}/>
    )
  }</WrapInput>
)

export default class ExampleForm extends React.Component {

  handleSubmit = (data) => {
    console.log('data', data)
  }

  render() {
    return (
      <ReformContext themes={{default: defaultTheme}} validations={defaultValidations}>
        <div>
          <h4>Form</h4>
          <Form onSubmit={this.handleSubmit} style={{paddingBottom: 300}}>
            <DatePicker name="startDate" dateFormat="DD-MM-YYYY" isRequired/>
          </Form>
        </div>
      </ReformContext>
    )
  }
}

See it in action

Form

is required
Check your console to see the submitted value

Belle's date picker

import React from 'react'
import ReactDOM from 'react-dom'
import {Form, ReformContext, WrapInput} from 'react-reform'
import defaultValidations from 'react-reform/opt/validations'
import defaultTheme from '../default-theme'
import {DatePicker as RawDatePicker} from 'belle'
import 'react-date-picker/index.css'

const DatePicker = props => (
  <WrapInput type="DatePicker" directProps={props} focusFn={node => ReactDOM.findDOMNode(node).focus()}>{
    ({value, themeProps: {onChange, ...remainingThemeProps}}) => (
      <RawDatePicker value={value || null} onUpdate={e => onChange(e.value)} {...remainingThemeProps}/>
    )
  }</WrapInput>
)

export default class ExampleForm extends React.Component {

  handleSubmit = (data) => {
    console.log('data', data)
  }

  render() {
    return (
      <ReformContext themes={{default: defaultTheme}} validations={defaultValidations}>
        <div>
          <h4>Form</h4>
          <Form onSubmit={this.handleSubmit}>
            <DatePicker name="startDate" isRequired/>
          </Form>
        </div>
      </ReformContext>
    )
  }
}

See it in action

Form

March 2017
SuMoTuWeThFrSa
262728123456789101112131415161718192021222324252627282930311
is required
Check your console to see the submitted value

react-bootstrap-daterangepicker

import React from 'react'
import {Form, ReformContext, WrapInput} from 'react-reform'
import defaultValidations from 'react-reform/opt/validations'
import defaultTheme from '../default-theme'
import RawDateRangePicker from 'react-bootstrap-daterangepicker'
import 'react-bootstrap-daterangepicker/css/daterangepicker.css'

const DateRangePicker = props => (
  <WrapInput type="DatePicker" directProps={props} focusFn={n => n.$picker.data('daterangepicker').show()}>{
    ({value, themeProps: {onChange, onFocus, onBlur, ...remainingThemeProps}}) => (
      <RawDateRangePicker
        startDate={(value && value.startDate) || undefined}
        endDate={(value && value.endDate) || undefined}
        onShow={onFocus}
        onHide={onBlur}
        onApply={(e, datePicker) => onChange({startDate: datePicker.startDate, endDate: datePicker.endDate})}
        {...remainingThemeProps}
      />
    )
  }</WrapInput>
)

export default class ExampleForm extends React.Component {

  handleSubmit = (data) => {
    console.log('data', data)
  }

  render() {
    return (
      <ReformContext themes={{default: defaultTheme}} validations={defaultValidations}>
        <div>
          <h4>Form</h4>
          <Form onSubmit={this.handleSubmit}>
            <DateRangePicker name="range" isRequired>
              <button type="button">Open picker</button>
            </DateRangePicker>
          </Form>
        </div>
      </ReformContext>
    )
  }
}

See it in action

Form

is required
Check your console to see the submitted value

Custom components

The recommended way would be to create one component first and wrap it rather than wrapping 3 inputs. But to give you a idea of what a very incomplete approach could look like:

import React from 'react'
import {Form, ReformContext, WrapInput} from 'react-reform'
import defaultValidations from 'react-reform/opt/validations'
import defaultTheme from '../default-theme'

const DatePicker = props => (
  <WrapInput type="DatePicker" directProps={props}>{
    ({value, themeProps: {onChange, onFocus, onBlur, ref, ...remainingThemeProps}}) => (
      <div {...remainingThemeProps}>
        <input type="number" value={(value && value.day) || ''}
          onChange={e => onChange({...value, day: e.target.value})}
          onFocus={onFocus} onBlur={onBlur} ref={ref}
          placeholder="day" style={{width: '3em'}}
        />
        <select value={(value && value.month) || ''}
          onChange={e => onChange({...value, month: e.target.value})}
          onFocus={onFocus} onBlur={onBlur}
        >
          {'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ').map((m, i) => (
            <option value={i + 1} key={i}>{m}</option>
          ))}
        </select>
        <input type="number" value={(value && value.year) || ''}
          onChange={e => onChange({...value, year: e.target.value})}
          onFocus={onFocus} onBlur={onBlur}
          placeholder="year" style={{width: '4em'}}
        />
      </div>
    )
  }</WrapInput>
)

export default class ExampleForm extends React.Component {

  handleSubmit = (data) => {
    console.log('data', data)
  }

  render() {
    return (
      <ReformContext themes={{default: defaultTheme}} validations={defaultValidations}>
        <div>
          <h4>Form</h4>
          <Form onSubmit={this.handleSubmit}>
            <DatePicker name="startDate" isRequired/>
          </Form>
        </div>
      </ReformContext>
    )
  }
}

See it in action

Form

is required
Check your console to see the submitted value
React Reform is brought to you by Codecks. Suggest edits for these pages on GitHub