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
propsLet'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()}
.
(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.Let's take a look at various DatePickers to see how we can wrap them.
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>
)
}
}
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>
)
}
}
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>
)
}
}
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>
)
}
}
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>
)
}
}