{"action":"created","issue":{"url":"https://api.github.com/repos/erikras/redux-form/issues/726","repository_url":"https://api.github.com/repos/erikras/redux-form","labels_url":"https://api.github.com/repos/erikras/redux-form/issues/726/labels{/name}","comments_url":"https://api.github.com/repos/erikras/redux-form/issues/726/comments","events_url":"https://api.github.com/repos/erikras/redux-form/issues/726/events","html_url":"https://github.com/erikras/redux-form/issues/726","id":139831979,"number":726,"title":"redux-form v5 API Proposal","user":{"login":"erikras","id":4396759,"avatar_url":"https://avatars.githubusercontent.com/u/4396759?v=3","gravatar_id":"","url":"https://api.github.com/users/erikras","html_url":"https://github.com/erikras","followers_url":"https://api.github.com/users/erikras/followers","following_url":"https://api.github.com/users/erikras/following{/other_user}","gists_url":"https://api.github.com/users/erikras/gists{/gist_id}","starred_url":"https://api.github.com/users/erikras/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/erikras/subscriptions","organizations_url":"https://api.github.com/users/erikras/orgs","repos_url":"https://api.github.com/users/erikras/repos","events_url":"https://api.github.com/users/erikras/events{/privacy}","received_events_url":"https://api.github.com/users/erikras/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/erikras/redux-form/labels/%3C-------%20READ%20THIS","name":"<------- READ THIS","color":"fbca04"},{"url":"https://api.github.com/repos/erikras/redux-form/labels/discussion","name":"discussion","color":"5319e7"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":57,"created_at":"2016-03-10T09:27:03Z","updated_at":"2016-03-28T22:14:53Z","closed_at":null,"body":"Greetings all,\r\n\r\nThere are a number of feature requests, bugs, and performance issues that have caused me to rethink the design of `redux-form`. I'd like to use this issue as a way to introduce you to some of my thinking, prepare you for what's to come, and get your feedback. Hopefully at least one of the features will make you excited to migrate to `v5` when it comes out.\r\n\r\n# `redux-form` `v5` API Proposal\r\n\r\n#### Glossary\r\n> __connected__: subscribed to changes in the Redux store with [`connect()`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options)\r\n\r\n## 1. The form should not be connected\r\n\r\nWhen writing the initial API, I provided things that seemed convenient for small forms, like having `this.props.values` be the current state of the values of your form. This required having the wrapping form component to be connected to the Redux store, ___and to re-render on every value change___. This doesn't matter if it's just a `username` and `password` form, but some of you are writing complex CRM systems with potentially hundreds of fields on the page at a time, causing `redux-form` to grind to a halt. #529 \r\n\r\nSo, what does this mean?\r\n\r\n* The convenience functionality of providing `mapStateToProps` and `mapDispatchToProps` is going away. You'll have to use `connect()` directly yourself.\r\n* But if the form isn't connected, then...\r\n\r\n## 2. Each field is connected\r\n\r\nEach field should subscribe _only_ to the very specific slice of the Redux state that it needs to know how to render itself. This means that `redux-form` can no longer only provide you with props to give to your own inputs.\r\n\r\n```js\r\nimport React, { Component } from 'react'\r\nimport { reduxForm } from 'redux-form'\r\n\r\n@reduxForm({\r\n  form: 'login',\r\n  fields: [ 'username', 'password' ]\r\n})\r\nexport default class LoginForm extends Component {\r\n  render() {\r\n    const { fields: { username, password } } = this.props\r\n    return (\r\n      <form>\r\n        <input type=\"text\" {...username}/>     // <--- can no longer use this syntax\r\n        <input type=\"password\" {...password}/> // <--- can no longer use this syntax\r\n      </form>\r\n    )\r\n  }\r\n}\r\n```\r\n\r\nSo what do we do? The destructured props was one of the most elegant things about `redux-form`!\r\n\r\nThere are __three options__ that I want to support:\r\n\r\n### 2.1. On-Demand Field Structure\r\n\r\nThe `redux-form` user base seems pretty divided on how much they like providing the list of fields for the entire form, so I want to provide away to arbitrarily specify the path to your field at render-time. This can be done with a `<Field/>` component that is given the name of the field in `dot.and[1].bracket` syntax.\r\n\r\n```js\r\nimport React, { Component } from 'react'\r\nimport { reduxForm, Field } from 'redux-form'\r\n\r\n@reduxForm({\r\n  form: 'userInfo' // <----- LOOK! No fields!\r\n})\r\nexport default class UserInfoForm extends Component {\r\n  render() {\r\n    return (\r\n      <form>\r\n        <Field\r\n          name=\"name\" // <---- path to this field in the form structure\r\n          component={React.DOM.input} // <---- type of component to render\r\n          type=\"text\"/> // <--- any additional props to pass on to above component\r\n\r\n        <Field name=\"age\" component={React.DOM.input} type=\"number\"/>\r\n\r\n        <Field name=\"preferences.favoriteColor\" component={React.DOM.select}>\r\n          <option value=\"#ff0000\">Red</option> // <--- children as if it were a <select>\r\n          <option value=\"#00ff00\">Green</option>\r\n          <option value=\"#0000ff\">Blue</option>\r\n        </Field>\r\n      </form>\r\n    )\r\n  }\r\n}\r\n```\r\n\r\n#### 2.1.1. Custom Input Components\r\n\r\nAnother key feature of `redux-form` is the ability to very simply attach custom input components. Let's imagine a hypothetical input component called `SpecialGeolocation` that requires the following props:\r\n\r\n> `location` - the location to display, in a colon-delimited `'lat:long'` string\r\n> `onUpdateLocation` - a callback that is given the new `'lat:long'` string\r\n\r\nLet's further complicate the matter by requiring that we save our location in a `{ latitude, longitude }` json object. How would we use this component in `redux-form` `v4.x` and `v5`?\r\n\r\nWell, given the extra string-to-json requirement, we need a complimentary pair of `format` and `parse` functions. We need these in ___both___ `v4.x` and `v5`!\r\n\r\n```js\r\nconst format = jsonLocation => `${jsonLocation.latitude}:${jsonLocation.longitude}`\r\nconst parse = colonDelimitedLocation => {\r\n  const tokens = colonDelimitedLocation.split(':')\r\n  return { latitude: tokens[0], longitude: tokens[1] }\r\n}\r\n```\r\nIn `v4.x` we map the props manually...\r\n```js\r\n// v4.x\r\nconst { fields: { myLocation } } = this.props;\r\n// ...\r\n<SpecialLocation\r\n  location={format(myLocation.value)}\r\n  onUpdateLocation={value => myLocation.onChange(parse(value))}/>\r\n```\r\n...and in `v5` we have to specify a `mapProps` function to do it.\r\n```js\r\n// v5\r\n<Field\r\n  name=\"myLocation\"\r\n  component={SpecialLocation}\r\n  mapProps={props => ({\r\n    location: format(props.value),\r\n    onUpdateLocation: value => props.onChange(parse(value))\r\n  })}/>\r\n```\r\n\r\nSimple enough, don't you think?\r\n\r\n### 2.2. Provided field structure\r\n\r\nThe benefit of providing your fields array as a config parameter or prop to your decorated component is that `redux-form`, as it does in `v4.x`, can provide you with the connected field components as props. So, whereas the `this.props.fields` provided to `v4.x` were _the props_ to your input components (`value`, `onChange`, etc.), in `v5` they are now the `Field` components (see 2.1 above) which will apply those props to an input component of your choosing.\r\n\r\n```js\r\nimport React, { Component } from 'react'\r\nimport { reduxForm } from 'redux-form'\r\n\r\n@reduxForm({\r\n  form: 'userInfo',\r\n  fields: [ 'name', 'age', 'preferences.favoriteColor' ]\r\n})\r\nexport default class UserInfoForm extends Component {\r\n  render() {\r\n    const { fields: { name, age, preferences } } = this.props\r\n    return (\r\n      <form>\r\n        <name component={React.DOM.input} type=\"text\"/>\r\n        <age component={React.DOM.input} type=\"number\"/>\r\n        <preferences.favoriteColor component={React.DOM.select}>\r\n          <option value=\"#ff0000\">Red</option>\r\n          <option value=\"#00ff00\">Green</option>\r\n          <option value=\"#0000ff\">Blue</option>\r\n        </preferences.favoriteColor>\r\n      </form>\r\n    )\r\n  }\r\n}\r\n```\r\n\r\n#### 2.2.1. Custom Components\r\n\r\nYou can do the same thing as described in 2.1.1 above with these fields.\r\n\r\n```js\r\n<myLocation\r\n  component={SpecialLocation}\r\n  mapProps={props => ({\r\n    location: format(props.value),\r\n    onUpdateLocation: value => props.onChange(parse(value))\r\n  })}/>\r\n```\r\n\r\n### 2.3. Convenience Inputs\r\n\r\nAll that `component={React.DOM.whatever}` boilerplate is pretty ugly. In `v5`, I want to provide convenience properties. These will be provided using memoized [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get), so they will only be constructed if you use them. The use of getters means saying a not-so-fond farewell to IE8 support. [React is dropping its support](http://facebook.github.io/react/blog/2016/01/12/discontinuing-ie8-support.html), and this library will as well.\r\n\r\nLook how concise and elegant this is:\r\n\r\n```js\r\nimport React, { Component } from 'react'\r\nimport { reduxForm } from 'redux-form'\r\n\r\n@reduxForm({\r\n  form: 'userInfo',\r\n  fields: [ 'name', 'age', 'preferences.favoriteColor' ]\r\n})\r\nexport default class UserInfoForm extends Component {\r\n  render() {\r\n    const { fields: { name, age, preferences } } = this.props\r\n    return (\r\n      <form>\r\n        <name.text/>  // <input type=\"text\"/>\r\n        <age.number/> // <input type=\"number\"/>\r\n        <preferences.favoriteColor.select>\r\n          <option value=\"#ff0000\">Red</option>\r\n          <option value=\"#00ff00\">Green</option>\r\n          <option value=\"#0000ff\">Blue</option>\r\n        </preferences.favoriteColor.select>\r\n      </form>\r\n    )\r\n  }\r\n}\r\n```\r\nAll of the types of `<input/>` will be supported as well as `<select>` and `<textarea>`. These will be able to handle special quirks about how checkboxes or radio buttons interact with data.\r\n\r\n### 2.3.1. Submit and Reset buttons\r\n\r\nJust like the fields, `this.props.submit` and `this.props.reset` button components will also be made available, with flags for common functionality you might expect, e.g. `disableWhenInvalid`. More on form submission below.\r\n \r\n## 3. Displaying Errors\r\n\r\nAlso as a getter on the field objects, like in 2.3 above, there will be an `error` component that will output a `<div>` with the error message.\r\n\r\n```js\r\n  render() {\r\n    const { fields: { name, city, country } } = this.props\r\n    return (\r\n      <form>\r\n        <name.text/>\r\n        <name.error/> // will only show if there is an error and the \"name\" field is \"touched\"\r\n        <city.text/>\r\n        <city.error showUntouched/> // will always show if there is an error\r\n        <country.text/>\r\n        <country.hasError> // allows for larger structure around error\r\n          <div>\r\n            <strong>OMG, FIX THIS!</strong>\r\n            <country.error/>\r\n          </div>\r\n        </country.hasError>\r\n      </form>\r\n    )\r\n  }\r\n```\r\n\r\n## 4. Sync Validation\r\n\r\nThe move in `v5` to decentralize the power from the outer form element to the fields themselves proves a problem for how `redux-form` has historically done sync validation, as the form component cannot rerender with every value change.\r\n\r\n### 4.1. Sync Validate Entire Record\r\n\r\nTraditionally, your sync validation function has been given the entire record. I would still like to have a \"validate this entire record\" functionality, but it is going to have to move to the reducer, and the reducer will store the sync validation errors in the Redux state.\r\n\r\n### 4.2. Sync Validate Individual Fields\r\n\r\nMany `redux-form` users over its lifetime have requested the ability to put `required` or `maxLength` validation props directly on the input components. Now the `redux-form` is controlling the field components, it makes perfect sense to do this. `v5` will attempt to use the same props as defined in the [HTML5 Validity](http://www.html5rocks.com/en/tutorials/forms/constraintvalidation/) spec, _as well as_ to set the errors directly onto the DOM nodes with `setCustomValidity()` as defined by said spec. #254.\r\n\r\n## 5. Async Validation\r\n\r\nFor async validation, you will have to specify which fields will need to be sent to the async validation function so that `redux-form` can create a non-rendering connected component to listen for when those fields change, similar to `asyncBlurFields` works in `v4.x`, except that the async validation function will only receive the fields specified, not the whole record.\r\n\r\n### 5.1. Debounce\r\n\r\nI'm not certain of the API yet, but some mechanism for allowing async validation to fire on _change_ (after a set delay), not just on blur, will be provided. I'm open to suggestions.\r\n\r\n## 6. Normalizing\r\n\r\nSince sync validation is moving to the field components, I think it makes sense for normalizers to be there as well. Something like:\r\n\r\n```js\r\n<username.text normalize={value => value.toUppercase()}/>\r\n```\r\n\r\nWhat I liked about normalizing being on the reducer in `v4` was that your value would get normalized even if it was modified from some non-field dispatch of a `CHANGE` action, but I don't think that was happening very often in practice. It can certainly be added back in the future if need be.\r\n\r\n## 7. Formatters and Parsers\r\n\r\nFormatters and parsers are the first cousins to normalizers. Say you want to display a phone number text field in the format `(212) 555-4321`, but you want to store the data as just numbers, `2125554321`. You could write functions to do that.\r\n\r\n```js\r\n<shipping.phone\r\n  format={value => prettyPhone(value)}\r\n  parse={value => value.replace(/[^0-9]/g, '')}/> // strip non numbers\r\n```\r\n\r\nThe __formatter__ is taking the raw data from the store and making it pretty for display in the input, and the __parser__ takes the pretty input value and converts it back to the ugly store data.\r\n\r\n## 8. ImmutableJS\r\n\r\nMany of you have been holding your breath so far hoping I'd get to this. :smile: \r\n\r\nI've gone back and forth and back and forth on this topic. I rewrote the whole reducer to use ImmutableJS, and the rewrote it in plain js objects several times. I'm certain of the following things:\r\n\r\n* ImmutableJS has a very important role to play in Redux and making fast React applications\r\n* It's not faster than plain objects for small structures\r\n* A library like `redux-form` should __NOT__ require that its users keep their Redux state as ImmutableJS objects\r\n* Using ImmutableJS internally and giving the state back with toJS() is a terrible idea.\r\n\r\nSo, in conclusion, I'm officially forbidding the use of ImmutableJS with `redux-form`. Deal with it.\r\n\r\n---\r\n\r\n:stuck_out_tongue_winking_eye: :stuck_out_tongue_winking_eye: Nah, I'm just kidding. I've actually solved this problem. :stuck_out_tongue_winking_eye: :stuck_out_tongue_winking_eye: \r\n\r\nIf you do this...\r\n```js\r\nimport { reduxForm } from 'redux-form'\r\n```\r\n...then `redux-form` will keep all its internal state in plain javascript objects, doing shallow copies up and down the tree to not mutate the objects that don't need to change (be rerendered).\r\n\r\nBut, if you do this...\r\n```js\r\nimport { reduxForm } from 'redux-form/immutable'\r\n```\r\n...then `redux-form` will keep ___all its internal state___ in ImmutableJS objects.\r\n\r\nI've managed to hide the internal state structure behind a minimal `getIn`/`setIn` façade enabling the same reducer code to work on both types. This has taken up most of my effort on `v5` so far, but it's working beautifully with extensive tests.\r\n\r\n## 9. Separation of Values and Form State\r\n\r\nAs a library user you shouldn't care too much about this, but many of the problems and bugs (#628, #629, #662) that `v4.x` has suffered from have been a result of the internal state structure being like this:\r\n\r\n```js\r\n// example state in v4.x\r\n{\r\n  firstName: {\r\n    initial: 'Erik'\r\n    value: 'Eric',    \r\n    touched: true,\r\n    submitError: 'Not the most awesome spelling'\r\n  },\r\n  lastName: {\r\n    initial: '',\r\n    value: 'Rasmussen',\r\n    touched: true,\r\n    submitError: 'Sounds too Scandinavian'\r\n  }\r\n}\r\n```\r\nIn `v5`, the initial values, current values, and field state will be kept separate.\r\n```js\r\n// example state in v5\r\n{\r\n  initial: {\r\n    firstName: 'Erik',\r\n    lastName: ''\r\n  },\r\n  values: {\r\n    firstName: 'Eric',\r\n    lastName: 'Rasmussen'\r\n  },\r\n  fields: {\r\n    firstName: {\r\n      touched: true,\r\n      submitError: 'Not the most awesome spelling'\r\n    },\r\n    lastName: {\r\n      touched: true,\r\n      submitError: 'Sounds too Scandinavian'\r\n    }\r\n  }\r\n}\r\n```\r\nThis will make the `RESET` action literally just do this:\r\n```js\r\nreturn {\r\n  values: state.initial,\r\n  initial: state.initial\r\n}\r\n```\r\nIt will also enable things like subscribing to all the form values if one choses to. Such a decorator will be provided if only to enable the library demos.\r\n\r\n## 10. Form Submission\r\n\r\n\"But!\", you say, \"If the form component isn't connected, how can it submit the values from the form component?\"\r\n\r\nThis was a big problem with this inversion of control, but I have [invented a way](https://github.com/erikras/redux-spy) for a large outer component to be able to read values from the Redux store _without being directly connected_, and therefore rerendering on every value change. This _should_ allow for a `handleSubmit()` paradigm similar to `v4.x`, but I haven't gotten to proving that yet.\r\n\r\n## 11. What is going away?\r\n\r\nI think the only functionality that going away is `formKey` and `reduxMountPoint`\r\n\r\n### 11.1. `formKey`\r\n\r\n`formKey` dates back to before you could specify `form` as a prop (it used to only be a config parameter). It was designed to allow you to use the same form shape multiple times on a page, but you can do that with `form`. The only thing stopping the removal of `formKey` before was normalizers, but since they are moving to the fields (see 6 above), `formKey` can be laid to rest.\r\n\r\n### 11.2. `reduxMountPoint`\r\n\r\n`reduxMountPoint` was obsoleted by the more flexible `getFormState()`, which `v5` will still have.\r\n\r\n## 12. Documentation, Examples, and Website\r\n\r\nI'm not happy with how the examples are currently implemented on the `redux-form` site. I like how the [Redux examples](https://github.com/reactjs/redux/tree/master/examples) are each in their own little folder that can be run individually. For `v5`, I want to meet the following goals for the docs:\r\n\r\n* Documentation exists in the `master` branch in a way that is navigable entirely on Github\r\n* Examples in their own folder in the `master` branch in a way that can be run locally with `npm install` and `npm start`\r\n* That same documentation (literally the same markdown files) navigable on redux-form.com\r\n* Those same examples running in separate little single-page apps on redux-form.com. They will be separate apps to demonstrate how some of them use third party libraries, like ImmutableJS or MaterialUI.\r\n* Enable anchor linking to specific parts of the docs. This means the docs can't be a single-page app with React Router anymore, due to github.io forcing the use of `hashHistory`.\r\n* redux-form.com hosted on github.io, which only hosts static files.\r\n\r\nThat is not an easy requirements list to hit, but I've figured out a way.\r\n\r\n## 13. Roadmap\r\n\r\nThe following will not make it into the release of `v5`, but `v5` is being designed with these future features in mind.\r\n\r\n### 13.1. Custom Library Extensions\r\n\r\nIn the same way that the standard inputs in `React.DOM` will be provided as convenience properties (see 2.3 above), `redux-form` `v5` will (eventually) allow extensions to be applied via third party extension libraries to make it easy to use a third party input library. So imagine something like this:\r\n\r\n```js\r\nimport { reduxForm } from 'redux-form'\r\nimport reduxFormMaterialUi from 'redux-form-material-ui'\r\nimport MenuItem from 'material-ui/lib/menus/menu-item'\r\n\r\nreduxForm.extend(reduxFormMaterialUi)\r\n...\r\n<form>\r\n  <name.muiTextField hintText=\"Hint text\"/>\r\n  <meeting.start.muiTimePicker format=\"24hr\"/>\r\n  <preferences.favoriteColor.muiSelectField>\r\n    <MenuItem value=\"#ff0000\" primaryText=\"Red\"/>\r\n  </preferences.favoriteColor.muiSelectField>\r\n</form>\r\n```\r\nReact Native is the most obvious extension, but any input library could be done. [React Bootstrap](http://react-bootstrap.github.io), [React Select](http://jedwatson.github.io/react-select/), [Belle](http://nikgraf.github.io/belle/), [Date Pickers](https://react.rocks/tag/DatePicker), Wysiwyg editors, etc. could all be done in this way.\r\n\r\nOne thing I'm not 100% sure about is whether or not extensions should be allowed to overwrite the built-in convenience components, like `text` or `password`, or if they should be prefixed as in my example above. Opinions welcome.\r\n\r\n### 13.2. HTML5 Validity\r\n\r\nIt's possible that the actual `setCustomValidation()` stuff mentioned in 4.1 above will not make the initial release.\r\n\r\n# Conclusion\r\n\r\nAs you can see, I've been putting some thought into this. Thank you for taking the time to read through it all. If you see any glaring mistakes or think something should be done differently, I am very open to constructive criticism. I'm pretty excited about these changes, and I hope you are, too."},"comment":{"url":"https://api.github.com/repos/erikras/redux-form/issues/comments/202601335","html_url":"https://github.com/erikras/redux-form/issues/726#issuecomment-202601335","issue_url":"https://api.github.com/repos/erikras/redux-form/issues/726","id":202601335,"user":{"login":"davidkpiano","id":1093738,"avatar_url":"https://avatars.githubusercontent.com/u/1093738?v=3","gravatar_id":"","url":"https://api.github.com/users/davidkpiano","html_url":"https://github.com/davidkpiano","followers_url":"https://api.github.com/users/davidkpiano/followers","following_url":"https://api.github.com/users/davidkpiano/following{/other_user}","gists_url":"https://api.github.com/users/davidkpiano/gists{/gist_id}","starred_url":"https://api.github.com/users/davidkpiano/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davidkpiano/subscriptions","organizations_url":"https://api.github.com/users/davidkpiano/orgs","repos_url":"https://api.github.com/users/davidkpiano/repos","events_url":"https://api.github.com/users/davidkpiano/events{/privacy}","received_events_url":"https://api.github.com/users/davidkpiano/received_events","type":"User","site_admin":false},"created_at":"2016-03-28T22:14:53Z","updated_at":"2016-03-28T22:14:53Z","body":"One thing I'm confused about is this:\r\n\r\n```js\r\n        <Submit component={button}\r\n          disableWhenPristine\r\n          submittingClassName=\"submitting\">Submit</Submit>\r\n```\r\n\r\nThis seems very proprietary to Redux Form. Even though it's a little more code, why try to abstract this?\r\n\r\n```js\r\n// formState (or however it's named) is where\r\n// the state of the form reducer is\r\nconst { formState, handleSubmit } = props;\r\n\r\n// ...\r\n\r\n<Submit component={button}\r\n  disabled={!formState.pristine}\r\n  className={formState.submitting ? 'submitting' : ''}>\r\n  Submit\r\n</Submit>\r\n```\r\n\r\nOne of the biggest issues with Redux Form < v5 was that it tried to own too much. I think it would be a great improvement if it leaned more towards a \"just use vanilla JS/React/Redux\" approach. :smile: "}}