React Forms
React components for binding @reactables/forms
reactables.
Installation
npm i @reactables/react-forms
Form
type HookedRxForm = HookedReactable<ControlModels.Form<unknown>, RxFormActions>;
Form
is the provider component giving child Field
and FormArray
child components access to a HookedRxForm
.
import { build, group, control } from '@reactables/forms';
import { useReactable } from '@reactables/react';
import { Form, Field } from '@reactables/react-forms';
import Input from './Input';
const userConfig = group({
controls: {
name: control(['', 'required']),
email: control(['', ['required', 'email']]),
},
});
const MyForm = () => {
const rxForm = useReactable(() => build(userConfig));
return (
<Form rxForm={rxForm}>
<Field name="name" label="Name:" component={Input} />
<Field name="email" label="Email: " component={Input} />
</Form>
);
};
export default MyForm;
Field
A wrapper component that connects a component to the reactable form.
Below is an Input
component that will be wrapped by Field
and provided the FormControl<T>
via the meta
prop.
WrappedFieldInputProps
are passed in by the input
prop which contains the input value and event handlers.
import { WrappedFieldProps } from '@reactables/react-forms';
const Input = ({
input,
label,
meta: { touched, errors, pending, valid },
}: { label?: string } & WrappedFieldProps) => {
return (
<div className="mb-3">
{label && (
<label className={`form-label ${touched && !valid ? 'text-danger' : ''}`}>{label}</label>
)}
<input
{...input}
type="text"
className={`form-control ${touched && !valid ? 'is-invalid' : ''}`}
/>
{touched && errors.required && (
<div>
<small className="text-danger">Field is required</small>
</div>
)}
{touched && errors.email && (
<div>
<small className="text-danger">Not a valid email address</small>
</div>
)}
</div>
);
};
export default Input;
Continuing from our Form
example we can wrap the Input
component above as follows:
import { build, group, control } from '@reactables/forms';
import { useReactable } from '@reactables/react-helpers';
import { Form, Field } from '@reactables/react-forms';
import Input from './Input';
const userConfig = group({
controls: {
name: control(['', 'required']),
email: control(['', ['required', 'email']]),
},
});
const MyForm = () => {
const rxForm = useReactable(() => build(userConfig));
return (
<Form rxForm={rxForm}>
<Field name="name" label="Name:" component={Input} />
<Field name="email" label="Email: " component={Input} />
</Form>
);
};
export default MyForm;
FormArray
FormArray
uses the function as children pattern and makes available the array
items as well as pushControl
and removeControl
action methods.
import { build, group, control, array } from '@reactables/forms';
import { useReactable } from '@reactables/react-helpers';
import { Form, Field, FormArray } from '@reactables/react-forms';
import Input from './Input';
import { RxFormArray, userConfig } from './RxFormArray';
const MyForm = () => {
const [state, actions] = useReactable(RxFormArray);
if (!state) return <></>;
return (
<Form rxForm={[state, actions]}>
<FormArray name="contacts">
{({ items, pushControl, removeControl }) => {
return (
<>
{items.map((control, index) => {
return (
<div key={control.key}>
<div>Contact # {index + 1}</div>
<Field
name={`contacts.${index}.name`}
label="Name:"
component={Input}
/>
<Field
name={`contacts.${index}.email`}
label="Email: "
component={Input}
/>
<button type="button" onClick={() => removeControl(index)}>
Remove contact
</button>
</div>
);
})}
<button type="button" onClick={() => pushControl(userConfig)}>
Add Contact
</button>
</>
);
}}
</FormArray>
</Form>
);
};
export default MyForm;