{"id":13481283,"url":"https://github.com/bradwestfall/informative","last_synced_at":"2025-03-27T11:32:10.599Z","repository":{"id":57273776,"uuid":"84663416","full_name":"bradwestfall/informative","owner":"bradwestfall","description":"React Forms with ease. Use render-props to broadcast state changes for the Form and Field. Also first-class support for re-usable FieldWraps","archived":true,"fork":false,"pushed_at":"2019-03-14T07:04:38.000Z","size":191,"stargazers_count":18,"open_issues_count":4,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-30T14:43:44.428Z","etag":null,"topics":["forms","react","state"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bradwestfall.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-11T16:22:51.000Z","updated_at":"2023-01-28T05:42:28.000Z","dependencies_parsed_at":"2022-08-26T12:33:15.816Z","dependency_job_id":null,"html_url":"https://github.com/bradwestfall/informative","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradwestfall%2Finformative","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradwestfall%2Finformative/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradwestfall%2Finformative/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradwestfall%2Finformative/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bradwestfall","download_url":"https://codeload.github.com/bradwestfall/informative/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245836318,"owners_count":20680352,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["forms","react","state"],"created_at":"2024-07-31T17:00:50.440Z","updated_at":"2025-03-27T11:32:10.241Z","avatar_url":"https://github.com/bradwestfall.png","language":"JavaScript","funding_links":[],"categories":["Components"],"sub_categories":["Forms"],"readme":"# ** NOT MAINTAINED **\n\nWhen I wrote this library, redux-form was \"king of the land\" as far as React forms go. I wanted a similar API that provided meta state about forms and fields but without the redux part. This project did a great job of that. However, another project called [Formik](https://github.com/jaredpalmer/formik) emerged since I wrote `informative` and it does all the things I was trying to do and a few more. It's well maintained and I've recently updated my largest project to use it instead.\n\n\u003chr /\u003e\n\n# informative\n\nInformative is a `\u003cForm\u003e` and `\u003cField\u003e` Compound Component Stratagy that keep track of form and field state for you and can provide it to you in a several of ways. Create basic fields with a \"redux-form\" style `\u003cField /\u003e` API, or use render props to create a [re-usable \"Field Wrap\"](#field-wraps) with `\u003cField render\u003e`.\n\n## Install\n\n```sh\nnpm install --save informative\n```\n\n\n## Contents\n\n- [`\u003cForm\u003e` and `\u003cField\u003e`](#form-and-field)\n  - [`\u003cField component=\"input\" /\u003e`](#field-componentinput-)\n  - [`\u003cField component=\"select\" /\u003e`](#field-componentselect-)\n  - [`\u003cField component=\"textarea\" /\u003e`](#field-componenttextarea-)\n  - [Built-in Fields](#built-in-fields)\n  - [Custom Fields](#custom-fields)\n  - [`onChange` for `\u003cField\u003e` and `\u003cForm\u003e`](#onchange-for-field-and-form)\n- [Examples](#examples)\n  - [Basic Usage](#basic-usage)\n  - [Basic State Access](#basic-state-access)\n  - [Submit Handling](#submit-handling)\n  - [Redirects](#redirects)\n  - [Validation](#validation)\n  - [Top Level Access to `formState`](#top-level-access-to-formstate)\n  - [Initial Values](#initial-values)\n  - [Reset Form](#reset-form)\n  - [Field Wraps](#field-wraps)\n  - [Field Abstraction](#field-abstraction)\n  - [`connectField` HoC](#connectfield-hoc)\n- [`formState`](#formstate)\n- [`fieldState`](#fieldstate)\n\n\n# `\u003cForm\u003e` and `\u003cField\u003e`\n\n`\u003cForm\u003e` and `\u003cField\u003e` are the basic building blocks of the API. Always use the `\u003cForm\u003e` component to start an informative form as it is the main entry-point into the API.\n\nThe `\u003cField\u003e` component is used to register each field in your form with the API. It also bootstraps state into the `Form` component. It always requires a `name` prop which must be unique for this specific form unless using radio buttons (in which the names should be the same). To designate what type of field will be created, pass a `component` prop. The `component` prop can be a string or a custom component. In this example we're showing how to use strings to make basic fields.\n\n```jsx\nimport React from 'react'\nimport { Form, Field } from 'informative'\n\nconst MyForm = props =\u003e (\n  \u003cForm\u003e\n    \u003cField name=\"fullName\" component=\"input\" /\u003e\n    \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n  \u003c/Form\u003e\n)\n```\n\n\n### `\u003cField component=\"input\" /\u003e`\n\nIn the previous example `\u003cField name=\"fullName\" component=\"input\" /\u003e` is used to make a DOM element similar to `\u003cinput type=\"text\" name=\"fullName\"\u003e`. The difference though is that since we're using `\u003cField\u003e`, the input is wired into the form's state and responds to DOM events like `onChange` and a few others.\n\nWhen using `\u003cField\u003e` this way with `component=\"input\"`, a `type` property can also be used to designate other input types similar to ordinary HTML. If no `type` is specified, the default is `text`.\n\nUnder the hood, `\u003cField\u003e` uses `\u003cInputField\u003e` when passing `component=\"input\"` into `\u003cField\u003e` (as long as the type isn't a radio or checkbox). See more on `\u003cInputField\u003e` below in [Built-in Fields](#built-in-fields).\n\n\n#### Checkbox Inputs\n\nFor checkboxes, be sure to specify a prop for `checked` always, even if the value is false, supply `checked={false}`. Also, checkboxes should always have a `value` prop. When the checkbox is checked, the value in the `value` prop is what is applied to the field in terms of the field's state value. If the checkbox is unchecked, the field's state value will be an empty string.\n\n```jsx\n\u003cField name=\"active\" component=\"input\" type=\"checkbox\" value=\"yes\" checked={false} /\u003e\n```\n\nUnder the hood, `\u003cField\u003e` uses `\u003cCheckboxField\u003e` when passing `component=\"input\" type=\"checkbox\"` into `\u003cField\u003e`. See more on `\u003cCheckboxField\u003e` below in [Built-in Fields](#built-in-fields).\n\n\n#### Radio Inputs\n\nFor radios, you do not need to always specify `checked` as you do with checkboxes. However, you always need to specify a `value` prop for each radio in the group. A radio group is two or more radio inputs that share the same `name`. In fact, this is the only type of `\u003cField\u003e` that can share a `name` with another `\u003cField\u003e`. The value reported by the field's state will be the value of the respective radio that is checked. If no radio is checked, the value in the field's state will be an empty string.\n\n```jsx\n\u003cField name=\"color\" component=\"input\" type=\"radio\" value=\"yellow\" checked /\u003e\n\u003cField name=\"color\" component=\"input\" type=\"radio\" value=\"blue\" /\u003e\n```\n\nUnder the hood, `\u003cField\u003e` uses `\u003cRadioField\u003e` when passing `component=\"input\" type=\"radio\"` into `\u003cField\u003e`. See more on `\u003cRadioField\u003e` below in [Built-in Fields](#built-in-fields).\n\n\n### `\u003cField component=\"select\" /\u003e`\n\nA `\u003cselect\u003e` field can be created by using `\u003cField\u003e` and passing the string \"select\" as the `component` prop:\n\n```jsx\n\u003cField name=\"state\" component=\"select\" value=\"az\"\u003e\n  \u003coption\u003eaz\u003c/option\u003e\n  \u003coption\u003eca\u003c/option\u003e\n\u003c/Field\u003e\n```\n\nThis is the one case where `\u003cField\u003e` passes child props\n\nUnder the hood, `\u003cField\u003e` uses `\u003cSelectField\u003e` when passing `component=\"select\"` into `\u003cField\u003e`. See more on `\u003cSelectField\u003e` below in [Built-in Fields](#built-in-fields).\n\n\n### `\u003cField component=\"textarea\" /\u003e`\n\nA `\u003ctextarea\u003e` field can be created by using `\u003cField\u003e` and passing the string \"textarea\" as the `component` prop:\n\n```jsx\n\u003cField name=\"state\" component=\"textarea\" value=\"Here is the starting text\" /\u003e\n```\n\nNotice that the starting text (if you choose to provide it) is applied using the `value` prop.\n\nUnder the hood, `\u003cField\u003e` uses `\u003cTextareaField\u003e` when passing `component=\"textarea\"` into `\u003cField\u003e`. See more on `\u003cTextareaField\u003e` below in [Built-in Fields](#built-in-fields).\n\n\n### Built-in Fields\n\nThere are several built-in fields which the API uses internally but can also be used by you. The fields are\n\n- InputField (for `\u003cinput\u003e`'s that aren't radio or checkboxes)\n- CheckboxField\n- RadioField\n- SelectField\n- TextareaField\n\nWhereas `\u003cField\u003e` can be used with a string `component` prop, it can also be passed a custom component that returns a field. For example, both of these fields are the same:\n\n```jsx\n\u003cField name=\"email\" component=\"input\" value=\"example@example.com\" /\u003e\n\u003cField name=\"email\" component={InputField} value=\"example@example.com\" /\u003e\n```\n\nAll of the built in fields can be imported from the `informative` module like this:\n\n```js\nimport { InputField, CheckboxField, RadioField, SelectField, TextareaField } from 'informative'\n```\n\n\n### Custom-Fields\n\nDocs coming soon!\n\n\n\n## `onChange` for `\u003cField\u003e` and `\u003cForm\u003e`\n\nSometimes you'll want real-time state updates as the user interacts with the form. By passing an `onChange` into `\u003cField\u003e`, the API will call your callback function whenever there is a change to the field. The arguments provided will be `fieldState`, `formState`, and the DOM event if applicable.\n\n```jsx\n\u003cField name=\"email\" input=\"input\" onChange={(fieldState, formState, event) =\u003e { ... }} /\u003e\n```\n\nYou can also pass an `onChange` callback into `\u003cForm\u003e`. The `\u003cForm\u003e`'s version on `onChange` will be provided the `formState`, then the `name` of the field that was changed, then the DOM event (if applicable).\n\n```jsx\n\u003cForm onChange={(formState, name, event) =\u003e { ... }} /\u003e\n```\n\nNote that the `\u003cField\u003e` change will also trigger the a `\u003cForm\u003e` change and the \u003cField\u003e change will be called first before the `\u003cForm\u003e`'s.\n\n\n\n# Examples\n\nThere are several examples you can run after cloning the repo. To run each one just type this command and follow the prompts:\n\n```sh\nnpm run examples\n```\n\nNavigate to [localhost:3030](http://localhost:3030) to view the example\n\n\n## Basic Usage\n\n[See Full Code Example](examples/basic-usage/index.js)\n\nIn this example, the most basic usage of `\u003cForm\u003e` and `\u003cField\u003e` are used. This form will behave just like a normal HTML form that has no submit or validation handlers (In other words, the form will submit to a new page and since there's no action attribute, it will submit to the same page). While there is no JS validation occurring in this example, HTML5 form validation will occur unless the `novalidate` attribute is passed into `\u003cform /\u003e` (Note that in React the actual value passed is `noValidate`).\n\n```jsx\nimport React from 'react'\nimport { Form, Field } from 'informative'\n\nconst RegistrationForm = props =\u003e (\n  \u003cForm\u003e\n    \u003cField name=\"email\" component=\"input\" type=\"email\" /\u003e\n    \u003cField name=\"password\" component=\"input\" type=\"password\" /\u003e\n    \u003cField name=\"state\" component=\"select\"\u003e\n      \u003coption\u003eaz\u003c/option\u003e\n      \u003coption\u003eca\u003c/option\u003e\n    \u003c/Field\u003e\n    \u003cField name=\"gender\" component=\"input\" type=\"radio\" value=\"male\" /\u003e\n    \u003cField name=\"gender\" component=\"input\" type=\"radio\" value=\"female\" /\u003e\n    \u003cField name=\"newsletter\" component=\"input\" type=\"checkbox\" value=\"yes\" checked /\u003e\n    \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n  \u003c/Form\u003e\n)\n```\n\nThe above example creates an ordinary HTML form like this:\n\n```html\n\u003cform\u003e\n  \u003cinput type=\"email\" name=\"email\" /\u003e\n  \u003cinput type=\"password\" name=\"password\" /\u003e\n  \u003cselect name=\"state\"\u003e\n    \u003coption\u003eaz\u003c/option\u003e\n    \u003coption\u003eca\u003c/option\u003e\n  \u003c/select\u003e\n  \u003cinput type=\"radio\" name=\"gender\" value=\"male\" /\u003e\n  \u003cinput type=\"radio\" name=\"gender\" value=\"female\" /\u003e\n  \u003cinput type=\"checkbox\" name=\"newsletter\" value=\"yes\" checked /\u003e\n\u003c/form\u003e\n```\n\n\n## Basic State Access\n\n[See Full Code Example](examples/basic-state-access/index.js)\n\nAs seen in the previous example, by passing a string value for the `component` prop, the API will build our `\u003cinput /\u003e` field for us. But that doesn't give us access to the field's state or the form's state.\n\nAs an alternative, we can pass a custom component into the `component` prop:\n\n```jsx\nconst InputField = props =\u003e {\n  const { name, type, events, fieldState, formState } = props\n\n  // Access to field and form state\n  console.log('Field State', fieldState)\n  console.log('Form State State', formState)\n\n  return \u003cinput name={name} type={type} {...events} /\u003e\n}\n\nconst Example = props =\u003e (\n  \u003cForm\u003e\n    \u003cField name=\"email\" component={InputField} type=\"email\" /\u003e\n  \u003c/Form\u003e\n)\n```\n\nBy providing the component our `InputField` instead of a string, we will gain access to props like `fieldState` and `formState` from within `InputField`. `InputField` is now expected to return a valid HTML form element of your choice (it doesn't have to be `\u003cinput /\u003e`). Just be sure to spread the `events` prop into your element to ensure the correct event callbacks are applied.\n\n\u003e Note: You may need to configure babel to understand JSX spread.\n\n\n## Submit Handling\n\n[See Full Code Example](examples/submit-handling/index.js)\n\nTo provide custom submit handling, pass an `onSubmit` callback prop into `Form`. The `onSubmit` callback gets called when the form is submitted and passes the form's `values` and `formState` to your callback function.\n\n```jsx\nclass LoginForm extends React.Component {\n\n  onSubmit(values, formState) {\n    console.log('Values', values)\n    console.log('Form State', formState)\n\n    // Always return a promise to let the API know the status\n    // of your submission.\n    return Promise.resolve()\n  }\n\n  render() {\n    return (\n      \u003cForm onSubmit={this.onSubmit}\u003e\n        \u003cField name=\"email\" component=\"input\" type=\"email\"/\u003e\u003cbr /\u003e\n        \u003cField name=\"password\" component=\"input\" type=\"password\" /\u003e\u003cbr /\u003e\n        \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n      \u003c/Form\u003e\n    )\n  }\n}\n```\n\nNote that your submit handler callback will not be called if the form is invalid based on your validation rules. Validation and rules are discussed later in this document.\n\n`onSubmit` is expected to return a promise. This will tell the API whether any asynchronous operations were successful or not. When a rejected promise is returned to the API, the `formState` will reflect these new changes: `{ submitting: false, submitFailed: true }`\n\n\n## Redirects\n\nIf your form needs to redirect after submission, the appropriate way to handle this is to return a promise with the first resolved `.then()` doing the redirect:\n\n```js\nonSubmit(values) {\n  return someNetworkRequest.then(() =\u003e {\n    history.push('/new-page')\n  })\n}\n```\n\n\u003e This example assumes the `history` API from React Router.\n\nSince our API for `onSubmit` is going to handle your return promise and set the form's state based on whether it was resolved or rejected, there could be a potential race condition where `setState` is called after the component has unmounted (causing an error because the redirect unmounted the form). But the API knows how to handle this race condition if you perform the redirect inside the first resolved `then()` before returning the promise from `onSubmit`. This race condition bug fix was implemented on v0.2.3\n\n\n## Validation\n\n[See Full Code Example](examples/validation/index.js)\n\nTo provide custom validation, pass a `validate` callback prop into `Form`. The `validate` callback gets called with every value change of any field along with `formState` for reference. It receives the form's values as an argument and is expected to return an error object with each field's name as a property and a value that corresponds to the error. Note that only fields that have errors should be returned in the error object and if there are no errors, an empty object should be returned.\n\n```jsx\nclass LoginForm extends React.Component {\n\n  validate(values, formState) {\n    const errors = {}\n    if (!/^[\\w\\d\\.]+@[\\w\\d]+\\.[\\w]{2,9}$/.test(values.email)) errors.email = 'Invalid Email'\n    if (!/^[\\w\\d]{6,20}$/.test(values.password)) errors.password = 'Invalid Password'\n    return errors\n  }\n\n  render() {\n    return (\n      \u003cForm validate={this.validate}\u003e\n        \u003cField name=\"email\" component=\"input\" /\u003e\u003cbr /\u003e\n        \u003cField name=\"password\" component=\"input\" type=\"password\" /\u003e\u003cbr /\u003e\n        \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n      \u003c/Form\u003e\n    )\n  }\n}\n```\n\nThere are much more elegant ways of writing validation such that you wouldn't have to re-write rules for every form, but this example shows a proof-of-concept for filling an error object with errors for email and password:\n\n```json\n{ \"email\": \"Invalid Email\", \"password\": \"Invalid Password\" }\n```\n\nYou each field in the error object, you can return a string if you wish, or you can return any other value. Your returned error object represents you notion of errors and the API will give you the same values back in any place where `fieldState` or `formState` is returned.\n\n\n## Top-Level Access to `formState`\n\nSometimes it's nice to know what the `formState` is at the top-level of the API. For instance, what if we want to disable the submit button based on whether the form is valid or if the form is in the middle of a long submit?\n\nThe `Form` component allows us to pass a `render` callback function:\n\n```jsx\nclass LoginForm extends React.Component {\n\n  validate(values) {\n    const errors = {}\n    if (!/^[\\w\\d\\.]+@[\\w\\d]+\\.[\\w]{2,9}$/.test(values.email)) errors.email = 'Invalid Email'\n    if (!/^[\\w\\d]{6,20}$/.test(values.password)) errors.password = 'Invalid Password'\n    return errors\n  }\n\n  render() {\n    return (\n      \u003cForm validate={this.validate} render={(formProps, formState) =\u003e (\n        \u003cform {...formProps}\u003e\n          \u003cField name=\"email\" component={Input} /\u003e\u003cbr /\u003e\n          \u003cField name=\"password\" component={Input} type=\"password\" /\u003e\u003cbr /\u003e\n          \u003cbutton type=\"submit\" disabled={!formState.validForm || formState.submitting}\u003eSubmit\u003c/button\u003e\n        \u003c/form\u003e\n      )} /\u003e\n    )\n  }\n}\n```\n\nThe only catch is that we now need to return a `\u003cform\u003e` element since using the API this way won't provide it for us. On a side note, if you need to customize the actual DOM `\u003cform\u003e` element beyond what the API builds, this is the way to do that.\n\nThe `render` callback is given `formProps` which is required to spread over the `\u003cform\u003e` for events required by the API. The callback is also given `formState` which is the primary reason to use this pattern -- to have state access at the form level.\n\nNote that we could have also used the `formState` parameter to put error messages next to each field. But this document will show a better way to do that later in the **Field Wraps** section.\n\n\n## Initial Values\n\n[See Full Code Example](examples/initial-values/index.js)\n\nThe `\u003cField\u003e` component can take a `value` prop to provide fields with their initial values:\n\n```jsx\nclass EditUser extends React.Component {\n\n  constructor() {\n    super()\n    this.state = {}\n  }\n\n  componentWillMount() {\n    // Simulate network latency\n    setTimeout(() =\u003e {\n      this.setState({\n        email: 'example@example.com',\n        password: 'abc123'\n      })\n    }, 500)\n  }\n\n  render() {\n    return (\n      \u003cForm\u003e\n        \u003cField name=\"email\" component=\"input\" value={this.state.email}/\u003e\u003cbr /\u003e\n        \u003cField name=\"password\" type=\"password\" component=\"input\" value={this.state.password}/\u003e\u003cbr /\u003e\n        \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n      \u003c/Form\u003e\n    )\n  }\n}\n```\n\nNote that while the form does allow late values to be passed into the form (because sometimes values come late from async operations) , `informative` does not work by allowing you to manage your form's state outside the form and then by passing values into fields as they change. All form state is managed internally with callback options to be alerted on state changes (See later in this document).\n\n\n## Reset Form\n\n[See Full Code Example](examples/reset-form/index.js)\n\nIf you wish to reset a form, use `refs` to access the API's internal `resetForm` method like this:\n\n```jsx\nclass UpdatePassword extends React.Component {\n  static _myform\n\n  constructor() {\n    super()\n    this.resetForm = this.resetForm.bind(this)\n  }\n\n  resetForm() {\n    // The \u003cForm /\u003e component has a `resetForm` method that you can access via refs\n    this._myform.resetForm()\n  }\n\n  render() {\n    return (\n      \u003cForm ref={form =\u003e {this._myform = form}} validate={this.validate}\u003e\n        \u003cField name=\"Name\" component=\"input\" value=\"Brad\" /\u003e\u003cbr /\u003e\n        \u003cField name=\"bio\" component=\"textarea\" value=\"Web Development\" /\u003e\u003cbr /\u003e\n        \u003cField name=\"state\" component=\"select\" value=\"az\"\u003e\n          \u003coption\u003e\u003c/option\u003e\n          \u003coption\u003eaz\u003c/option\u003e\n          \u003coption\u003eca\u003c/option\u003e\n        \u003c/Field\u003e\u003cbr /\u003e\n        \u003cField name=\"active\" component=\"input\" type=\"checkbox\" value=\"yes\" checked /\u003e\u003cbr /\u003e\n        \u003cField name=\"gender\" component=\"input\" type=\"radio\" value=\"m\" checked /\u003e\n        \u003cField name=\"gender\" component=\"input\" type=\"radio\" value=\"f\" /\u003e\u003cbr /\u003e\n        \u003cbutton type=\"button\" onClick={this.resetForm}\u003eReset\u003c/button\u003e\n      \u003c/Form\u003e\n    )\n  }\n}\n```\n\n\n## Field Wraps\n\n[See Full Code Example](examples/field-wraps/index.js)\n\nGenerally speaking, you probably want some sort of consistent wrapping DOM around all your fields. The specifics of yours may differ, but the concept is probably similar to this HTML:\n\n```jsx\n\u003cdiv class=\"field-wrap\"\u003e\n  \u003clabel\u003eEmail\u003c/label\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003cField name=\"email\" component={Input} type=\"email\" /\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"field-wrap\"\u003e\n  \u003clabel\u003ePassword\u003c/label\u003e\n  \u003cdiv class=\"field\"\u003e\n    \u003cField name=\"password\" component={Input} type=\"password\"/\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n```\n\nTo achieve this without repeating the same HTML structure by hard-coding it for each `Field`, create a custom component to use in your forms that wraps around `\u003cField\u003e`:\n\n```jsx\nimport React from 'react'\nimport { Form, Field, InputField } from 'informative'\n\nconst FieldWrap = props =\u003e {\n  const { label, component: Component, children, name, value, ...rest } = props\n\n  return (\n    \u003cField {...rest} name={name} value={value} render={(events, fieldState, formState) =\u003e {\n      return (\n        \u003cdiv className=\"field-wrap\"\u003e\n          \u003clabel htmlFor={`field-${name}`}\u003e{label}\u003c/label\u003e\n          \u003cdiv className=\"field\"\u003e\n            \u003cComponent {...rest} originalValue={value} name={name} fieldState={fieldState} formState={formState} events={events}\u003e\n              {children}\n            \u003c/Component\u003e\n          \u003c/div\u003e\n          \u003cdiv className=\"error\"\u003e\n            {fieldState.error}\n          \u003c/div\u003e\n        \u003c/div\u003e\n      )\n    }} /\u003e\n  )\n}\n\nconst LoginForm = props =\u003e (\n  \u003cForm\u003e\n    \u003cFieldWrap label=\"Email\" name=\"email\" component={InputField} /\u003e\n    \u003cFieldWrap label=\"Password\" name=\"password\" component={InputField} type=\"password\" /\u003e\n    \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n  \u003c/Form\u003e\n)\n```\n\nNot only do we get to write the wrapper code and use it everywhere, it has other benefits like how field errors can be emitted inline with the field in the DOM.\n\nYou can build your own version of `\u003cFieldWrap\u003e` any way you like. This one is just an example and doesn't come with the API. Notice that this one does make use of the built-in `InputField` component. This example `\u003cFieldWrap\u003e` would allow us to use the build-in `InputField`, `CheckboxField`, `RadioField`, `SelectField`, and `TextareaField` -- all of which require specific props that you can see we're passing into `\u003cComponent originalValue={value} name={name} fieldState={fieldState} formState={formState} events={events} /\u003e`. Yours though could be built any way you like, just be sure to pass the `events` prop into the actual form field to wire it into the API.\n\nFor more docs on the built-in fields, see [Built-in Fields](#built-in-fields) above.\n\n\n## Field Abstractions\n\n[See Full Code Example](examples/full-featured/index.js)\n\nFurther abstraction can be done by making specific types of fields for quick use. For example, imaging having a `\u003cFieldFirstName /\u003e` component which provides the same result as `\u003cFieldWrap label=\"First Name\" name=\"firstName\" component={InputField} /\u003e`.\n\nWith our new `FieldWrap` component, we can now make easy field abstractions for common fields:\n\n```jsx\nconst FieldFirstName = props =\u003e \u003cFieldWrap label=\"First Name\" name=\"firstName\" component={InputField} {...props} /\u003e\nconst FieldLastName = props =\u003e \u003cFieldWrap label=\"Last Name\" name=\"lastName\" component={InputField} {...props} /\u003e\nconst FieldEmail = props =\u003e \u003cFieldWrap label=\"Email\" name=\"email\" component={InputField} type=\"email\" {...props} /\u003e\nconst FieldPassword = props =\u003e \u003cFieldWrap label=\"Password\" name=\"password\" component={InputField} type=\"password\" {...props} /\u003e\n```\n\nTo be used like this:\n\n```jsx\nconst SignupForm = props =\u003e (\n  \u003cForm\u003e\n    \u003cFieldFirstName /\u003e\n    \u003cFieldLastName /\u003e\n    \u003cFieldEmail /\u003e\n    \u003cFieldEmail label=\"Repeat Email\" name=\"repeatEmail\" /\u003e\n    \u003cFieldPassword /\u003e\n  \u003c/Form\u003e\n)\n```\n\nSince we spread the props over `\u003cFieldWrap\u003e`, each custom field can be overridden as we do with the second email in this example\n\n\n## `connectField` HoC\n\nIf you'd rather use a higher order component to create a \"Field Wrap\" concept instead of using `\u003cField\u003e` with a callback child, the `connectField` can be used like this:\n\n```jsx\nimport { Form, connectField } from 'informative'\n\nconst Input = props =\u003e {\n  const { name, type, input } = props\n  return \u003cinput type={type || 'text'} id={`field-` + name} name={name} {...input} /\u003e\n}\n\nconst FieldWrap = props =\u003e {\n  const { input, fieldState, formState, label, type, name } = props\n\n  // Access to field and form state\n  console.log('Field State', fieldState)\n  console.log('Form State State', formState)\n\n  return (\n    \u003cdiv className=\"field-wrap\"\u003e\n      \u003clabel htmlFor={`field-` + name}\u003e{label}\u003c/label\u003e\n      \u003cdiv className=\"input\"\u003e\n        \u003cInput input={input} name={name} type={type} /\u003e\n      \u003c/div\u003e\n      \u003cdiv className=\"error\"\u003e\n        {fieldState.error}\n      \u003c/div\u003e\n    \u003c/div\u003e\n  )\n}\n\n// High-level abstraction fields created with `connectField` HoC\nconst FieldEmail = connectField('email')(FieldWrap)\nconst FieldPassword = connectField('password')(FieldWrap)\n\nclass LoginForm extends React.Component {\n\n  validate(values) {\n    const errors = {}\n    if (!/^[\\w\\d\\.]+@[\\w\\d]+\\.[\\w]{2,9}$/.test(values.email)) errors.email = 'Invalid Email'\n    if (!/^[\\w\\d]{6,20}$/.test(values.password)) errors.password = 'Invalid Password'\n    return errors\n  }\n\n  render() {\n    return (\n      \u003cForm validate={this.validate}\u003e\n        \u003cFieldEmail label=\"Email\" /\u003e\n        \u003cFieldPassword label=\"Password\" name=\"password\" type=\"password\" /\u003e\n        \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n      \u003c/Form\u003e\n    )\n  }\n}\n```\n\n\n# `formState`\n\nA primary goal of this API is to provide your form with real-time form-state changes. The API keeps state in one object for the entire form with the following properties with these initial values:\n\n```js\nformState = {\n  hasSubmitted: false,\n  submitFailed: false,\n  submitting: false,\n  validForm: true,\n  visited: false,\n  dirty: false,\n  errors: {},\n  fields: {},\n  values: {}\n}\n```\n\n\n#### hasSubmitted [`boolean: false`]\n\nHas the form been submitted yet? This defaults to `false` and is set to `true` when the form is submitted regardless of the response from the `onSubmit` callback or the response from the `validate` callback. This is set back to `false` after a call to `resetForm()`.\n\n#### submitFailed [`boolean: false`]\n\nDid the form fail submission in it's last attempt? This defaults to `false` and is set to `true` if the user-supplied validation fails or if the promise returned from the `onSubmit` callback is rejected. Once `true`, this value is set to `false` again when the form is submitted and user-supplied validation passes and the returned promise from the `onSubmit` callback is resolved. This is set back to `false` after a call to `resetForm()`.\n\n#### submitting [`boolean: false`]\n\nIs the form in the process of submitting? This defaults to `false` and is set to `true` when the form is submitted and if the user-supplied validation succeeds. This is set back to `false` when the returned promise from the `onSubmit` callback is rejected or resolved. This is set back to `false` after a call to `resetForm()`.\n\n#### validForm [`boolean: true`]\n\nIs the form valid according to the user-supplied validation callback? This defaults to `true` and is set to `false` any time the user-supplied validation callback returns an object with keys (representing errors).\n\nUpon submission, if this value is set to `false` then the user-supplied submit callback will not be called and the form will receive new state reflecting these changes `{ submitting: false, submitFailed: true, hasSubmitted: true }`\n\n#### visited [`boolean: false`]\n\nHave any of the form's fields been visited? This defaults to `false` and is changed to `true` when any field in the form has its `onChange`, `onBlur`, or `onFocus` events fire. This does not get set back to `false`, even after a call to `formReset()`.\n\n#### dirty [`boolean: false`]\n\nHave any of the form's fields been changed? This defaults to `false` and is changed to `true` when any field in the form has its `onChange` event fired. This is set back to `false` after a call to `resetForm()`.\n\n#### errors [`object`]\n\nThis is an object that will either be empty when there are no errors, or will be filled with errors as supplied by the return value of the user-supplied validate callback. The presence of keys in this object is what will trigger `validForm` to be `false`\n\n#### fields [`object`]\n\nThis is an object with one property for each field registered in the form. See more about this object in **fieldState** below.\n\n#### values [`object`]\n\nThis is an object with one property for each field registered in the form. The value of each property is the respective value for each field.\n\n#### resetForm() [`function`]\n\nThis function can be called to reset the form. Resetting a form will have the effect of resetting these `formState` values:\n\n```js\n{ hasSubmitted: false, submitFailed: false, submitting: false, dirty: false }\n```\n\nIt will also have the effect of resetting each field in `formState.fields` to it's default values (see below in **fieldState**) and setting each field in `formState.values` to have an empty string for its value.\n\n\n# `fieldState`\n\nEach field's state object is stored in `formState.fields[name]` where `name` is the provided name of each field. There are several instances in this API where `fieldState` and `formState` are provided to you in a callback. You can always derive the `fieldState` by digging into `formState`, but when `fieldState` is provided to you, it's just for convenience.\n\nEach field's respective state has these default values:\n\n```js\nfieldState = {\n  value: '',\n  error: '',\n  validField: true,\n  visited: false,\n  dirty: false,\n  active: false,\n  touched: false\n}\n```\n\n#### value [`string: ''`]\n\nThis is the field's value which defaults to an empty string. This value is changed in real-time as the user interacts with the field and the field's `onChange` event is fired.\n\n#### error [`string: ''`]\n\nThis is the error message or value given to this field by the user-supplied validation response. While the default value is an empty string, validation occurs immediately when the form is loaded which might cause field errors. This value is changed in real-time as the user interacts with the field and the field's onChange event is fired.\n\n#### validField [`boolean: true`]\n\nIs the field valid according to the user-supplied validation callback for the form? This defaults to `true` and is set to `false` any time the user-supplied validation callback returns an object with keys matching a field's name, this value will be changed to `false`.\n\n#### visited [`boolean: false`]\n\nHas this field been visited? This defaults to `false` and is changed to `true` when the field's `onChange`, `onBlur`, or `onFocus` events fire. This is set back to `false` after a call to `resetForm()`.\n\n#### dirty [`boolean: false`]\n\nHas this field been changed? This defaults to `false` and is changed to `true` when the field's `onChange` event is fired. This is set back to `false` after a call to `resetForm()`.\n\n#### active [`boolean: false`]\n\nIs the field active (has focus)? This defaults to `false` and is changed to `true` when the field's `onFocus` event is fired. This is set back to `false` when the field's `onBlur` event is fired or after a call to `resetForm()`.\n\n#### touched [`boolean: false`]\n\nHas the field been visited and then left. In other words, this defaults to `false` and is changed to `true` when the field's `onBlur` event is fired. This is set back to `false` after a call to `resetForm()`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbradwestfall%2Finformative","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbradwestfall%2Finformative","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbradwestfall%2Finformative/lists"}