├── .eslintignore
├── src
├── util
│ ├── eventConsts.js
│ ├── getDisplayName.js
│ ├── prefixName.js
│ ├── shallowCompare.js
│ ├── eventMocks.js
│ └── __tests__
│ │ ├── getDisplayName.spec.js
│ │ ├── prefixName.spec.js
│ │ └── eventMocks.spec.js
├── events
│ ├── isEvent.js
│ ├── silenceEvents.js
│ ├── silenceEvent.js
│ ├── onChangeValue.js
│ ├── __tests__
│ │ ├── silenceEvent.spec.js
│ │ ├── isEvent.spec.js
│ │ └── onChangeValue.spec.js
│ └── getValue.js
├── __tests__
│ ├── setup.js
│ ├── reducer.destroy.spec.js
│ ├── addExpectations.js
│ ├── reducer.clearAsyncError.spec.js
│ ├── reducer.startSubmit.spec.js
│ ├── reducer.unregisterField.spec.js
│ ├── reducer.arrayShift.spec.js
│ ├── reducer.startAsyncValidation.spec.js
│ ├── reducer.registerField.spec.js
│ ├── reducer.clearSubmit.spec.js
│ ├── reducer.submit.spec.js
│ ├── reducer.arrayPop.spec.js
│ ├── reducer.arrayUnshift.spec.js
│ ├── defaultShouldAsyncValidate.spec.js
│ ├── reducer.setSubmitSuceeded.spec.js
│ └── fieldKeys.spec.js
├── isReactNative.js
├── selectors
│ ├── getFormValues.js
│ ├── getFormSyncErrors.js
│ ├── getFormSubmitErrors.js
│ ├── isInvalid.js
│ ├── isDirty.js
│ ├── isSubmitting.js
│ ├── hasSubmitFailed.js
│ ├── hasSubmitSucceeded.js
│ ├── isPristine.js
│ ├── isValid.js
│ └── __tests__
│ │ ├── isSubmitting.spec.js
│ │ ├── hasSubmitFailed.spec.js
│ │ ├── hasSubmitSucceeded.spec.js
│ │ └── getFormValues.spec.js
├── SubmissionError.js
├── isChecked.js
├── structure
│ ├── plain
│ │ ├── getIn.js
│ │ ├── deepEqual.js
│ │ ├── index.js
│ │ ├── setIn.js
│ │ ├── expectations.js
│ │ ├── splice.js
│ │ └── deleteIn.js
│ └── immutable
│ │ ├── deepEqual.js
│ │ ├── splice.js
│ │ ├── index.js
│ │ ├── expectations.js
│ │ └── setIn.js
├── defaultShouldValidate.js
├── values.js
├── Form.js
├── defaultShouldAsyncValidate.js
├── asyncValidation.js
├── generateValidator.js
├── hasError.js
├── formValueSelector.js
├── immutable.js
├── deleteInWithCleanUp.js
├── index.js
├── FormSection.js
├── fieldKeys.js
├── actionTypes.js
└── createFieldArrayProps.js
├── immutable.js
├── examples
├── simple
│ ├── .babelrc
│ ├── README.md
│ ├── devServer.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ ├── src
│ │ └── Simple.md
│ └── package.json
├── wizard
│ ├── .babelrc
│ ├── README.md
│ ├── src
│ │ ├── reducer.js
│ │ ├── renderField.js
│ │ ├── validate.js
│ │ ├── WizardFormFirstPage.js
│ │ ├── Wizard.md
│ │ ├── WizardForm.js
│ │ ├── WizardFormSecondPage.js
│ │ └── WizardFormThirdPage.js
│ ├── devServer.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ └── package.json
├── fieldArrays
│ ├── .babelrc
│ ├── README.md
│ ├── devServer.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ ├── src
│ │ ├── FieldArrays.md
│ │ └── validate.js
│ └── package.json
├── immutable
│ ├── .babelrc
│ ├── README.md
│ ├── src
│ │ ├── reducer.js
│ │ ├── ImmutableValues.js
│ │ ├── validate.js
│ │ ├── ImmutableForm.js
│ │ └── Immutable.md
│ ├── devServer.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ └── package.json
├── normalizing
│ ├── .babelrc
│ ├── README.md
│ ├── devServer.js
│ ├── src
│ │ ├── normalizePhone.js
│ │ └── FieldNormalizing.md
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ └── package.json
├── asyncValidation
│ ├── .babelrc
│ ├── README.md
│ ├── src
│ │ ├── validate.js
│ │ ├── reducer.js
│ │ ├── asyncValidate.js
│ │ ├── AsyncValidationForm.js
│ │ └── AsyncValidation.md
│ ├── devServer.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ └── package.json
├── remoteSubmit
│ ├── .babelrc
│ ├── README.md
│ ├── src
│ │ ├── RemoteSubmitButton.js
│ │ ├── submit.js
│ │ ├── RemoteSubmit.md
│ │ └── RemoteSubmitForm.js
│ ├── devServer.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ └── package.json
├── submitValidation
│ ├── .babelrc
│ ├── README.md
│ ├── devServer.js
│ ├── src
│ │ ├── submit.js
│ │ ├── SubmitValidationForm.js
│ │ └── SubmitValidation.md
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ └── package.json
├── syncValidation
│ ├── .babelrc
│ ├── README.md
│ ├── devServer.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ └── package.json
├── fieldLevelValidation
│ ├── .babelrc
│ ├── README.md
│ ├── devServer.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ ├── src
│ │ └── FieldLevelValidation.md
│ └── package.json
├── initializeFromState
│ ├── .babelrc
│ ├── README.md
│ ├── src
│ │ └── account.js
│ ├── devServer.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ └── package.json
├── selectingFormValues
│ ├── .babelrc
│ ├── README.md
│ ├── devServer.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ ├── package.json
│ └── src
│ │ └── SelectingFormValues.md
├── material-ui
│ ├── .babelrc
│ ├── README.md
│ ├── src
│ │ ├── asyncValidate.js
│ │ └── MaterialUi.md
│ ├── devServer.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ └── package.json
└── react-widgets
│ ├── .babelrc
│ ├── README.md
│ ├── devServer.js
│ ├── src
│ └── ReactWidgets.md
│ ├── package.json
│ ├── webpack.config.dev.js
│ └── webpack.config.prod.js
├── logo.png
├── docs
├── video-thumb.jpg
├── valueLifecycle.png
├── faq
│ ├── SubmitFunction.md
│ ├── ImmutableJs.md
│ ├── HandleVsOn.md
│ ├── README.md
│ ├── ReactNative.md
│ ├── CustomComponent.md
│ ├── EnterToSubmit.md
│ └── WebsocketSubmit.md
├── api
│ ├── SubmissionError.md
│ ├── Form.md
│ └── Reducer.md
└── ValueLifecycle.md
├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── babel-lodash-es.js
├── .travis.yml
├── .eslintrc
├── .editorconfig
├── tools.md
├── .babelrc
├── scripts
└── build-examples.sh
├── LICENSE
├── CONTRIBUTING.md
└── webpack.config.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | webpack.config*.js
2 | node_modules
3 |
--------------------------------------------------------------------------------
/src/util/eventConsts.js:
--------------------------------------------------------------------------------
1 | export const dataKey = 'text'
2 |
--------------------------------------------------------------------------------
/immutable.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/immutable');
2 |
--------------------------------------------------------------------------------
/examples/simple/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"]
3 | }
--------------------------------------------------------------------------------
/examples/wizard/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"]
3 | }
--------------------------------------------------------------------------------
/examples/fieldArrays/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"]
3 | }
--------------------------------------------------------------------------------
/examples/immutable/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"]
3 | }
--------------------------------------------------------------------------------
/examples/normalizing/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"]
3 | }
--------------------------------------------------------------------------------
/examples/asyncValidation/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"]
3 | }
--------------------------------------------------------------------------------
/examples/remoteSubmit/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"]
3 | }
--------------------------------------------------------------------------------
/examples/submitValidation/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"]
3 | }
--------------------------------------------------------------------------------
/examples/syncValidation/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"]
3 | }
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eligolding/redux-form/master/logo.png
--------------------------------------------------------------------------------
/examples/fieldLevelValidation/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"]
3 | }
--------------------------------------------------------------------------------
/examples/initializeFromState/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"]
3 | }
--------------------------------------------------------------------------------
/examples/selectingFormValues/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"]
3 | }
--------------------------------------------------------------------------------
/examples/material-ui/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015", "stage-2"]
3 | }
--------------------------------------------------------------------------------
/examples/react-widgets/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015", "stage-2"]
3 | }
--------------------------------------------------------------------------------
/docs/video-thumb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eligolding/redux-form/master/docs/video-thumb.jpg
--------------------------------------------------------------------------------
/docs/valueLifecycle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eligolding/redux-form/master/docs/valueLifecycle.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 | .nyc_output
4 | coverage
5 | node_modules
6 | dist
7 | lib
8 | es
9 | npm-debug.log
10 | .DS_Store
11 |
--------------------------------------------------------------------------------
/src/util/getDisplayName.js:
--------------------------------------------------------------------------------
1 | const getDisplayName = Comp => Comp.displayName || Comp.name || 'Component'
2 |
3 | export default getDisplayName
4 |
--------------------------------------------------------------------------------
/src/events/isEvent.js:
--------------------------------------------------------------------------------
1 | const isEvent = candidate => !!(candidate && candidate.stopPropagation && candidate.preventDefault)
2 |
3 | export default isEvent
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | examples
2 | scripts
3 | docs
4 | .babelrc
5 | .eslint*
6 | .idea
7 | .editorconfig
8 | .npmignore
9 | .nyc_output
10 | .travis.yml
11 | webpack.*
12 | coverage
13 |
--------------------------------------------------------------------------------
/examples/simple/README.md:
--------------------------------------------------------------------------------
1 | # Simple Form Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/examples/fieldArrays/README.md:
--------------------------------------------------------------------------------
1 | # Field Arrays Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/examples/immutable/README.md:
--------------------------------------------------------------------------------
1 | # Immutable JS Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/examples/normalizing/README.md:
--------------------------------------------------------------------------------
1 | # Field Normalizing Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/examples/remoteSubmit/README.md:
--------------------------------------------------------------------------------
1 | # Remote Submit Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/examples/wizard/README.md:
--------------------------------------------------------------------------------
1 | # Multi Page Wizard Form Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/src/__tests__/setup.js:
--------------------------------------------------------------------------------
1 | import { jsdom } from 'jsdom'
2 |
3 | global.document = jsdom('
')
4 | global.window = document.defaultView
5 | global.navigator = global.window.navigator
6 |
--------------------------------------------------------------------------------
/examples/material-ui/README.md:
--------------------------------------------------------------------------------
1 | # Simple Material UI Form Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/examples/submitValidation/README.md:
--------------------------------------------------------------------------------
1 | # Submit Validation Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/examples/syncValidation/README.md:
--------------------------------------------------------------------------------
1 | # Synchronous Validation Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/src/events/silenceEvents.js:
--------------------------------------------------------------------------------
1 | import silenceEvent from './silenceEvent'
2 |
3 | const silenceEvents = fn => (event, ...args) =>
4 | silenceEvent(event) ? fn(...args) : fn(event, ...args)
5 |
6 | export default silenceEvents
7 |
--------------------------------------------------------------------------------
/src/isReactNative.js:
--------------------------------------------------------------------------------
1 | const isReactNative =
2 | typeof window !== 'undefined' &&
3 | window.navigator &&
4 | window.navigator.product &&
5 | window.navigator.product === 'ReactNative'
6 |
7 | export default isReactNative
8 |
--------------------------------------------------------------------------------
/examples/asyncValidation/README.md:
--------------------------------------------------------------------------------
1 | # Asynchronous Blur Validation Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/examples/fieldLevelValidation/README.md:
--------------------------------------------------------------------------------
1 | # Field Level Validation Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/examples/initializeFromState/README.md:
--------------------------------------------------------------------------------
1 | # Initialize From State Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/examples/selectingFormValues/README.md:
--------------------------------------------------------------------------------
1 | # Selecting Form Values Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/examples/wizard/src/reducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { reducer as reduxFormReducer } from 'redux-form'
3 |
4 | const reducer = combineReducers({
5 | form: reduxFormReducer
6 | })
7 |
8 | export default reducer
9 |
--------------------------------------------------------------------------------
/src/selectors/getFormValues.js:
--------------------------------------------------------------------------------
1 | const createGetFormValues = ({ getIn }) =>
2 | (form, getFormState = state => getIn(state, 'form')) =>
3 | state => getIn(getFormState(state), `${form}.values`)
4 |
5 | export default createGetFormValues
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | This project adheres to [Semantic Versioning](http://semver.org/).
4 | Every release, along with the migration instructions, is documented on the Github [Releases](https://github.com/erikras/redux-form/releases) page.
5 |
--------------------------------------------------------------------------------
/examples/immutable/src/reducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux-immutablejs'
2 | import { reducer as form } from 'redux-form/immutable' // <--- immutable import
3 |
4 | const reducer = combineReducers({ form })
5 |
6 | export default reducer
7 |
--------------------------------------------------------------------------------
/src/events/silenceEvent.js:
--------------------------------------------------------------------------------
1 | import isEvent from './isEvent'
2 |
3 | const silenceEvent = event => {
4 | const is = isEvent(event)
5 | if (is) {
6 | event.preventDefault()
7 | }
8 | return is
9 | }
10 |
11 | export default silenceEvent
12 |
--------------------------------------------------------------------------------
/src/selectors/getFormSyncErrors.js:
--------------------------------------------------------------------------------
1 | const createGetFormSyncErrors = ({ getIn }) =>
2 | (form, getFormState = state => getIn(state, 'form')) =>
3 | state => getIn(getFormState(state), `${form}.syncErrors`)
4 |
5 | export default createGetFormSyncErrors
6 |
--------------------------------------------------------------------------------
/examples/react-widgets/README.md:
--------------------------------------------------------------------------------
1 | # [React Widgets](https://github.com/jquense/react-widgets) Example
2 |
3 | ## To run locally
4 |
5 | ```
6 | npm install
7 | npm start
8 | ```
9 |
10 | Then open [`http://localhost:3030/`](http://localhost:3030/).
11 |
--------------------------------------------------------------------------------
/src/selectors/getFormSubmitErrors.js:
--------------------------------------------------------------------------------
1 | const createGetFormSubmitErrors = ({ getIn }) =>
2 | (form, getFormState = state => getIn(state, 'form')) =>
3 | state => getIn(getFormState(state), `${form}.submitErrors`)
4 |
5 | export default createGetFormSubmitErrors
6 |
--------------------------------------------------------------------------------
/src/util/prefixName.js:
--------------------------------------------------------------------------------
1 | const isFieldArrayRegx = /\[\d+\]$/
2 |
3 | export default function formatName(context, name) {
4 | const { _reduxForm: { sectionPrefix } } = context
5 | return !sectionPrefix || isFieldArrayRegx.test(name) ? name : `${sectionPrefix}.${name}`
6 | }
7 |
--------------------------------------------------------------------------------
/src/SubmissionError.js:
--------------------------------------------------------------------------------
1 | import ExtendableError from 'es6-error'
2 |
3 | class SubmissionError extends ExtendableError {
4 | constructor(errors) {
5 | super('Submit Validation Failed')
6 | this.errors = errors
7 | }
8 | }
9 |
10 | export default SubmissionError
11 |
--------------------------------------------------------------------------------
/babel-lodash-es.js:
--------------------------------------------------------------------------------
1 | module.exports = function () {
2 | return {
3 | visitor: {
4 | ImportDeclaration(path) {
5 | const source = path.node.source
6 | source.value = source.value.replace(/^lodash($|\/)/, 'lodash-es$1')
7 | }
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | before_install:
4 | - npm install -g npm@latest
5 |
6 | node_js:
7 | - "6"
8 | - "5"
9 | - "4"
10 | - "stable"
11 |
12 | script:
13 | - npm run lint
14 | - npm test
15 |
16 | after_success:
17 | - npm run test:cov
18 | - npm run test:codecov
--------------------------------------------------------------------------------
/src/selectors/isInvalid.js:
--------------------------------------------------------------------------------
1 | import createIsValid from './isValid'
2 |
3 | const createIsInvalid = structure =>
4 | (form, getFormState) => {
5 | const isValid = createIsValid(structure)(form, getFormState)
6 | return state => !isValid(state)
7 | }
8 |
9 | export default createIsInvalid
10 |
--------------------------------------------------------------------------------
/src/selectors/isDirty.js:
--------------------------------------------------------------------------------
1 | import createIsPristine from './isPristine'
2 |
3 | const createIsDirty = structure =>
4 | (form, getFormState) => {
5 | const isPristine = createIsPristine(structure)(form, getFormState)
6 | return state => !isPristine(state)
7 | }
8 |
9 | export default createIsDirty
10 |
--------------------------------------------------------------------------------
/src/selectors/isSubmitting.js:
--------------------------------------------------------------------------------
1 | const createIsSubmitting = ({ getIn }) =>
2 | (form, getFormState = state => getIn(state, 'form')) =>
3 | state => {
4 | const formState = getFormState(state)
5 | return getIn(formState, `${form}.submitting`) || false
6 | }
7 |
8 | export default createIsSubmitting
9 |
--------------------------------------------------------------------------------
/src/util/shallowCompare.js:
--------------------------------------------------------------------------------
1 | import shallowEqual from 'shallowequal'
2 |
3 | const shallowCompare = (instance, nextProps, nextState) => {
4 | return (
5 | !shallowEqual(instance.props, nextProps) ||
6 | !shallowEqual(instance.state, nextState)
7 | )
8 | }
9 |
10 | export default shallowCompare
11 |
--------------------------------------------------------------------------------
/examples/asyncValidation/src/validate.js:
--------------------------------------------------------------------------------
1 | const validate = values => {
2 | const errors = {}
3 | if (!values.username) {
4 | errors.username = 'Required'
5 | }
6 | if (!values.password) {
7 | errors.password = 'Required'
8 | }
9 | return errors
10 | }
11 |
12 | export default validate
13 |
14 |
--------------------------------------------------------------------------------
/src/selectors/hasSubmitFailed.js:
--------------------------------------------------------------------------------
1 | const createHasSubmitFailed = ({ getIn }) =>
2 | (form, getFormState = state => getIn(state, 'form')) =>
3 | state => {
4 | const formState = getFormState(state)
5 | return getIn(formState, `${form}.submitFailed`) || false
6 | }
7 |
8 | export default createHasSubmitFailed
9 |
--------------------------------------------------------------------------------
/src/selectors/hasSubmitSucceeded.js:
--------------------------------------------------------------------------------
1 | const createHasSubmitSucceeded = ({ getIn }) =>
2 | (form, getFormState = state => getIn(state, 'form')) =>
3 | state => {
4 | const formState = getFormState(state)
5 | return getIn(formState, `${form}.submitSucceeded`) || false
6 | }
7 |
8 | export default createHasSubmitSucceeded
9 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint-config-rackt",
3 | "parser": "babel-eslint",
4 | "env": {
5 | "browser": true,
6 | "mocha": true,
7 | "node": true
8 | },
9 | "rules": {
10 | "react/jsx-uses-react": 2,
11 | "react/jsx-uses-vars": 2,
12 | "react/jsx-no-undef": 2
13 | },
14 | "plugins": [
15 | "react"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | insert_final_newline = true
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | trim_trailing_whitespace = true
9 |
10 | [*.md]
11 | trim_trailing_whitespace = false
12 |
13 | [*.js]
14 | indent_style = space
15 | indent_size = 2
16 | trim_trailing_whitespace = false
17 | insert_final_newline = true
18 | max_line_length = f
19 |
--------------------------------------------------------------------------------
/examples/wizard/src/renderField.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const renderField = ({ input, label, type, meta: { touched, error } }) => (
4 |
5 |
6 |
7 |
8 | {touched && error && {error}}
9 |
10 |
11 | )
12 |
13 | export default renderField
14 |
--------------------------------------------------------------------------------
/src/isChecked.js:
--------------------------------------------------------------------------------
1 | const isChecked = value => {
2 | if (typeof value === 'boolean') {
3 | return value
4 | }
5 | if (typeof value === 'string') {
6 | const lower = value.toLowerCase()
7 | if (lower === 'true') {
8 | return true
9 | }
10 | if (lower === 'false') {
11 | return false
12 | }
13 | }
14 | return undefined
15 | }
16 |
17 | export default isChecked
18 |
--------------------------------------------------------------------------------
/examples/asyncValidation/src/reducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { reducer as formReducer } from 'redux-form'
3 | import validate from './validate'
4 |
5 | const reducer = combineReducers({
6 | form: formReducer.validation({
7 | asyncValidation: validate // "asyncValidation" is the form name given to reduxForm() decorator
8 | })
9 | })
10 |
11 | export default reducer
12 |
--------------------------------------------------------------------------------
/examples/material-ui/src/asyncValidate.js:
--------------------------------------------------------------------------------
1 | const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
2 |
3 | const asyncValidate = (values/*, dispatch */) => {
4 | return sleep(1000) // simulate server latency
5 | .then(() => {
6 | if ([ 'foo@foo.com', 'bar@bar.com' ].includes(values.email)) {
7 | throw { email: 'Email already Exists' }
8 | }
9 | })
10 | }
11 |
12 | export default asyncValidate
13 |
14 |
--------------------------------------------------------------------------------
/tools.md:
--------------------------------------------------------------------------------
1 | # Tools
2 |
3 | The following libraries are either built on top of `redux-form` or are compatible with `redux-form`.
4 |
5 | * [`redux-form-generator`](https://github.com/lemonCMS/redux-form-generator)
6 | * [`redux-form-schema`](https://github.com/inlight-media/redux-form-schema)
7 | * [`redux-validate`](https://github.com/ashtonwar/redux-validate)
8 | * [`revalidate`](https://github.com/jfairbank/revalidate)
9 |
10 | Add yours!
11 |
--------------------------------------------------------------------------------
/src/selectors/isPristine.js:
--------------------------------------------------------------------------------
1 | const createIsPristine = ({ deepEqual, empty, getIn }) =>
2 | (form, getFormState = state => getIn(state, 'form')) =>
3 | state => {
4 | const formState = getFormState(state)
5 | const initial = getIn(formState, `${form}.initial`) || empty
6 | const values = getIn(formState, `${form}.values`) || initial
7 | return deepEqual(initial, values)
8 | }
9 |
10 | export default createIsPristine
11 |
--------------------------------------------------------------------------------
/examples/asyncValidation/src/asyncValidate.js:
--------------------------------------------------------------------------------
1 | const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
2 |
3 | const asyncValidate = (values/*, dispatch */) => {
4 | return sleep(1000) // simulate server latency
5 | .then(() => {
6 | if ([ 'john', 'paul', 'george', 'ringo' ].includes(values.username)) {
7 | throw { username: 'That username is taken' }
8 | }
9 | })
10 | }
11 |
12 | export default asyncValidate
13 |
14 |
--------------------------------------------------------------------------------
/src/structure/plain/getIn.js:
--------------------------------------------------------------------------------
1 | import { toPath } from 'lodash'
2 |
3 | const getIn = (state, field) => {
4 | if (!state) {
5 | return state
6 | }
7 |
8 | const path = toPath(field)
9 | const length = path.length
10 | if (!length) {
11 | return undefined
12 | }
13 |
14 | let result = state
15 | for (let i = 0; i < length && !!result; ++i) {
16 | result = result[path[i]]
17 | }
18 |
19 | return result
20 | }
21 |
22 | export default getIn
23 |
--------------------------------------------------------------------------------
/src/util/eventMocks.js:
--------------------------------------------------------------------------------
1 | const getEvent = rest => ({
2 | stopPropagation: id => id,
3 | preventDefault: id => id,
4 | ...rest
5 | })
6 |
7 | export function valueMock(value) {
8 | return getEvent({ target: { value } })
9 | }
10 |
11 | export function dragStartMock(setData) {
12 | return getEvent({
13 | dataTransfer: { setData }
14 | })
15 | }
16 |
17 | export function dropMock(getData) {
18 | return getEvent({
19 | dataTransfer: { getData }
20 | })
21 | }
22 |
--------------------------------------------------------------------------------
/docs/faq/SubmitFunction.md:
--------------------------------------------------------------------------------
1 | # My submit function isn't being called! Help?
2 |
3 | Possible causes:
4 |
5 | * Your synchronous validation function is not returning `{}`. Probably because:
6 | * You are upgrading from a previous version of `redux-form` that required that `{valid: true}` be returned.
7 | * You have removed a field from your form, but forgotten to remove it from your validation function.
8 | * Your asynchronous validation function is returning a rejected promise for some reason.
9 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015-no-commonjs", "react", "stage-2"],
3 | "env": {
4 | "development": {
5 | "plugins": ["lodash", "transform-es2015-modules-commonjs"]
6 | },
7 | "test": {
8 | "plugins": ["lodash", "transform-es2015-modules-commonjs", "istanbul"]
9 | },
10 | "production": {
11 | "plugins": ["lodash", "transform-es2015-modules-commonjs"]
12 | },
13 | "es": {
14 | "plugins": ["lodash", "./babel-lodash-es"]
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/defaultShouldValidate.js:
--------------------------------------------------------------------------------
1 | const defaultShouldValidate = ({
2 | values,
3 | nextProps,
4 | // props, // not used in default implementation
5 | initialRender,
6 | lastFieldValidatorKeys,
7 | fieldValidatorKeys,
8 | structure
9 | }) => {
10 | if (initialRender) {
11 | return true
12 | }
13 | return !structure.deepEqual(values, nextProps.values) ||
14 | !structure.deepEqual(lastFieldValidatorKeys, fieldValidatorKeys)
15 | }
16 |
17 | export default defaultShouldValidate
18 |
--------------------------------------------------------------------------------
/src/values.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 |
3 | const createValues = ({ getIn }) =>
4 | config => {
5 | const { form, prop, getFormState } = {
6 | prop: 'values',
7 | getFormState: state => getIn(state, 'form'),
8 | ...config
9 | }
10 | return connect(
11 | state => ({
12 | [prop]: getIn(getFormState(state), `${form}.values`)
13 | }),
14 | () => ({}) // ignore dispatch
15 | )
16 | }
17 |
18 | export default createValues
19 |
--------------------------------------------------------------------------------
/docs/faq/ImmutableJs.md:
--------------------------------------------------------------------------------
1 | # Does `redux-form` work with ImmutableJS?
2 |
3 | Yes!
4 |
5 | As of `v6`, `redux-form` has support for ImmutableJS built in.
6 |
7 | Simply import from a different endpoint and all of `redux-form`'s internal state will be kept
8 | with ImmutableJS data structures.
9 |
10 | Instead of...
11 | ```js
12 | import { reduxForm } from 'redux-form'
13 | ```
14 |
15 | ...do...
16 |
17 | ```js
18 | import { reduxForm } from 'redux-form/immutable'
19 | ```
20 |
21 | That's all there is to it!
22 |
--------------------------------------------------------------------------------
/src/util/__tests__/getDisplayName.spec.js:
--------------------------------------------------------------------------------
1 | import expect from 'expect'
2 | import getDisplayName from '../getDisplayName'
3 |
4 | describe('getDisplayName', () => {
5 | it('should read displayName', () => {
6 | expect(getDisplayName({ displayName: 'foo' })).toBe('foo')
7 | })
8 |
9 | it('should read name', () => {
10 | expect(getDisplayName({ name: 'foo' })).toBe('foo')
11 | })
12 |
13 | it('should default to Component', () => {
14 | expect(getDisplayName({ })).toBe('Component')
15 | })
16 | })
17 |
--------------------------------------------------------------------------------
/src/structure/plain/deepEqual.js:
--------------------------------------------------------------------------------
1 | import { isEqualWith } from 'lodash'
2 |
3 | const customizer = (obj, other) => {
4 | if (obj === other) return true
5 | if ((obj == null || obj === '' || obj === false) &&
6 | (other == null || other === '' || other === false)) return true
7 |
8 | if (obj && other && obj._error !== other._error) return false
9 | if (obj && other && obj._warning !== other._warning) return false
10 | }
11 |
12 | const deepEqual = (a, b) => isEqualWith(a, b, customizer)
13 |
14 | export default deepEqual
15 |
--------------------------------------------------------------------------------
/src/structure/plain/index.js:
--------------------------------------------------------------------------------
1 | import splice from './splice'
2 | import getIn from './getIn'
3 | import setIn from './setIn'
4 | import deepEqual from './deepEqual'
5 | import deleteIn from './deleteIn'
6 | import { some } from 'lodash'
7 |
8 | const structure = {
9 | empty: {},
10 | emptyList: [],
11 | getIn,
12 | setIn,
13 | deepEqual,
14 | deleteIn,
15 | fromJS: value => value,
16 | size: array => array ? array.length : 0,
17 | some,
18 | splice,
19 | toJS: value => value
20 | }
21 |
22 | export default structure
23 |
--------------------------------------------------------------------------------
/src/events/onChangeValue.js:
--------------------------------------------------------------------------------
1 | import getValue from './getValue'
2 | import isReactNative from '../isReactNative'
3 |
4 | const onChangeValue = (event, { name, parse, normalize }) => {
5 | // read value from input
6 | let value = getValue(event, isReactNative)
7 |
8 | // parse value if we have a parser
9 | if (parse) {
10 | value = parse(value, name)
11 | }
12 |
13 | // normalize value
14 | if (normalize) {
15 | value = normalize(name, value)
16 | }
17 |
18 | return value
19 | }
20 |
21 | export default onChangeValue
22 |
--------------------------------------------------------------------------------
/examples/initializeFromState/src/account.js:
--------------------------------------------------------------------------------
1 | // Quack! This is a duck. https://github.com/erikras/ducks-modular-redux
2 | const LOAD = 'redux-form-examples/account/LOAD'
3 |
4 | const reducer = (state = {}, action) => {
5 | switch (action.type) {
6 | case LOAD:
7 | return {
8 | data: action.data
9 | }
10 | default:
11 | return state
12 | }
13 | }
14 |
15 | /**
16 | * Simulates data loaded into this reducer from somewhere
17 | */
18 | export const load = data => ({ type: LOAD, data })
19 |
20 | export default reducer
21 |
--------------------------------------------------------------------------------
/examples/remoteSubmit/src/RemoteSubmitButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import { submit } from 'redux-form'
4 |
5 | const style = {
6 | padding: '10px 20px',
7 | width: 140,
8 | display: 'block',
9 | margin: '20px auto',
10 | fontSize: '16px'
11 | }
12 |
13 | const RemoteSubmitButton = ({ dispatch }) =>
14 |
18 | // ^^^^^^^^^^^^ name of the form
19 |
20 | export default connect()(RemoteSubmitButton)
21 |
--------------------------------------------------------------------------------
/docs/faq/HandleVsOn.md:
--------------------------------------------------------------------------------
1 | # What's the difference between `handleSubmit` and `onSubmit`?
2 |
3 | From what I can tell from every example I have seen, there is an unwritten – until now! – rule in the React community:
4 |
5 | > **handleX is what you name the function that you pass to the onX prop.**
6 |
7 | ```javascript
8 | render() {
9 | const handleClick = () => console.log('Clicked!');
10 | return ;
11 | }
12 | ```
13 |
14 | Since the only way that redux-form can provide you with functionality is to pass it as a prop, it passes you a
15 | `handleSubmit` for you to pass to `onSubmit`.
16 |
--------------------------------------------------------------------------------
/examples/wizard/src/validate.js:
--------------------------------------------------------------------------------
1 | const validate = values => {
2 | const errors = {}
3 | if (!values.firstName) {
4 | errors.firstName = 'Required'
5 | }
6 | if (!values.lastName) {
7 | errors.lastName = 'Required'
8 | }
9 | if (!values.email) {
10 | errors.email = 'Required'
11 | } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
12 | errors.email = 'Invalid email address'
13 | }
14 | if (!values.sex) {
15 | errors.sex = 'Required'
16 | }
17 | if (!values.favoriteColor) {
18 | errors.favoriteColor = 'Required'
19 | }
20 | return errors
21 | }
22 |
23 | export default validate
24 |
--------------------------------------------------------------------------------
/src/__tests__/reducer.destroy.spec.js:
--------------------------------------------------------------------------------
1 | import { destroy } from '../actions'
2 |
3 | const describeDestroy = (reducer, expect, { fromJS }) => () => {
4 | it('should destroy form state', () => {
5 | const state = reducer(fromJS({
6 | foo: {
7 | values: {
8 | myField: 'initialValue'
9 | },
10 | active: 'myField'
11 | },
12 | otherThing: {
13 | touchThis: false
14 | }
15 | }), destroy('foo'))
16 | expect(state)
17 | .toEqualMap({
18 | otherThing: {
19 | touchThis: false
20 | }
21 | })
22 | })
23 | }
24 |
25 | export default describeDestroy
26 |
--------------------------------------------------------------------------------
/scripts/build-examples.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | rebuild=false
3 | while test $# -gt 0; do
4 | case "$1" in
5 | --rebuild)
6 | rebuild=true
7 | shift
8 | ;;
9 | *)
10 | break
11 | ;;
12 | esac
13 | done
14 |
15 | if $rebuild
16 | then
17 | echo 'Rebuilding'
18 | fi
19 |
20 | cd examples
21 | for example in `ls -d */`
22 | do
23 | echo $example
24 | cd $example
25 | if $rebuild
26 | then
27 | rm -rf node_modules
28 | else
29 | rm -rf node_modules/redux-form
30 | rm -rf node_modules/redux-form-website-template
31 | fi
32 | npm install
33 | cd ..
34 | done
35 | tput bel
36 | say "The examples are built"
37 |
--------------------------------------------------------------------------------
/src/Form.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 |
3 | class Form extends Component {
4 | constructor(props, context) {
5 | super(props, context)
6 | if (!context._reduxForm) {
7 | throw new Error('Form must be inside a component decorated with reduxForm()')
8 | }
9 | }
10 |
11 | componentWillMount() {
12 | this.context._reduxForm.registerInnerOnSubmit(this.props.onSubmit)
13 | }
14 |
15 | render() {
16 | return
17 | }
18 | }
19 |
20 | Form.propTypes = {
21 | onSubmit: PropTypes.func.isRequired
22 | }
23 | Form.contextTypes = {
24 | _reduxForm: PropTypes.object
25 | }
26 |
27 | export default Form
28 |
--------------------------------------------------------------------------------
/src/structure/immutable/deepEqual.js:
--------------------------------------------------------------------------------
1 | import { Iterable } from 'immutable'
2 |
3 | import { isEqualWith } from 'lodash'
4 |
5 | const customizer = (obj, other) => {
6 | if (obj == other) return true
7 | if ((obj == null || obj === '' || obj === false) &&
8 | (other == null || other === '' || other === false)) return true
9 |
10 | if (Iterable.isIterable(obj) && Iterable.isIterable(other)) {
11 | return obj.count() === other.count() && obj.every((value, key) => {
12 | return isEqualWith(value, other.get(key), customizer)
13 | })
14 | }
15 |
16 | return void 0
17 | }
18 |
19 | const deepEqual = (a, b) => isEqualWith(a, b, customizer)
20 |
21 | export default deepEqual
22 |
--------------------------------------------------------------------------------
/src/defaultShouldAsyncValidate.js:
--------------------------------------------------------------------------------
1 | const defaultShouldAsyncValidate = ({
2 | initialized,
3 | trigger,
4 | // blurredField, // not used in default implementation
5 | pristine,
6 | syncValidationPasses
7 | }) => {
8 | if(!syncValidationPasses) {
9 | return false
10 | }
11 | switch(trigger) {
12 | case 'blur':
13 | // blurring
14 | return true
15 | case 'submit':
16 | // submitting, so only async validate if form is dirty or was never initialized
17 | // conversely, DON'T async validate if the form is pristine just as it was initialized
18 | return !pristine || !initialized
19 | default:
20 | return false
21 | }
22 | }
23 |
24 | export default defaultShouldAsyncValidate
25 |
--------------------------------------------------------------------------------
/examples/simple/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/wizard/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/fieldArrays/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/immutable/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/material-ui/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/normalizing/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/react-widgets/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/remoteSubmit/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/asyncValidation/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/remoteSubmit/src/submit.js:
--------------------------------------------------------------------------------
1 | import { SubmissionError } from 'redux-form'
2 |
3 | const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
4 |
5 | function submit(values) {
6 | return sleep(1000) // simulate server latency
7 | .then(() => {
8 | if (![ 'john', 'paul', 'george', 'ringo' ].includes(values.username)) {
9 | throw new SubmissionError({ username: 'User does not exist', _error: 'Login failed!' })
10 | } else if (values.password !== 'redux-form') {
11 | throw new SubmissionError({ password: 'Wrong password', _error: 'Login failed!' })
12 | } else {
13 | window.alert(`You submitted:\n\n${JSON.stringify(values, null, 2)}`)
14 | }
15 | })
16 | }
17 |
18 | export default submit
19 |
--------------------------------------------------------------------------------
/examples/submitValidation/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/syncValidation/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/fieldLevelValidation/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/initializeFromState/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/selectingFormValues/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3030, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3030');
27 | });
--------------------------------------------------------------------------------
/examples/submitValidation/src/submit.js:
--------------------------------------------------------------------------------
1 | import { SubmissionError } from 'redux-form'
2 |
3 | const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
4 |
5 | function submit(values) {
6 | return sleep(1000) // simulate server latency
7 | .then(() => {
8 | if (![ 'john', 'paul', 'george', 'ringo' ].includes(values.username)) {
9 | throw new SubmissionError({ username: 'User does not exist', _error: 'Login failed!' })
10 | } else if (values.password !== 'redux-form') {
11 | throw new SubmissionError({ password: 'Wrong password', _error: 'Login failed!' })
12 | } else {
13 | window.alert(`You submitted:\n\n${JSON.stringify(values, null, 2)}`)
14 | }
15 | })
16 | }
17 |
18 | export default submit
19 |
--------------------------------------------------------------------------------
/examples/immutable/src/ImmutableValues.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { values as valuesDecorator } from 'redux-form/immutable'
3 | import { Code } from 'redux-form-website-template'
4 |
5 | /**
6 | * This is just like the Values component that the other examples import, except that it works
7 | * with Immutable JS.
8 | */
9 | const ImmutableValues = ({ form }) => {
10 | const decorator = valuesDecorator({ form })
11 | const component = ({ values }) => {
12 | return (
13 |
14 |
Values
15 |
16 |
17 | )
18 | }
19 | const Decorated = decorator(component)
20 | return
21 | }
22 |
23 | export default ImmutableValues
24 |
--------------------------------------------------------------------------------
/src/asyncValidation.js:
--------------------------------------------------------------------------------
1 | import isPromise from 'is-promise'
2 |
3 | const asyncValidation = (fn, start, stop, field) => {
4 | start(field)
5 | const promise = fn()
6 | if (!isPromise(promise)) {
7 | throw new Error('asyncValidate function passed to reduxForm must return a promise')
8 | }
9 | const handleErrors = rejected => errors => {
10 | if (errors && Object.keys(errors).length) {
11 | stop(errors)
12 | return errors
13 | } else if (rejected) {
14 | stop()
15 | throw new Error('Asynchronous validation promise was rejected without errors.')
16 | }
17 | stop()
18 | return Promise.resolve()
19 | }
20 | return promise.then(handleErrors(false), handleErrors(true))
21 | }
22 |
23 | export default asyncValidation
24 |
--------------------------------------------------------------------------------
/docs/faq/README.md:
--------------------------------------------------------------------------------
1 | # Frequently Asked Questions
2 |
3 | Below is a list of common problems or questions that people have using `redux-form`.
4 |
5 | 1. [My submit function isn't being called! Help?](SubmitFunction.md)
6 |
7 | 2. [Will `redux-form` work with my custom input component?](CustomComponent.md)
8 |
9 | 3. [What's the difference between `handleSubmit` and `onSubmit`?](HandleVsOn.md)
10 |
11 | 4. [How can I clear my form after my submission succeeds?](HowToClear.md)
12 |
13 | 5. [How can I submit my form when the user presses Enter?](EnterToSubmit.md)
14 |
15 | 6. [Does `redux-form` work with React Native?](ReactNative.md)
16 |
17 | 7. [Does `redux-form` work with ImmutableJS?](ImmutableJs.md)
18 |
19 | 8. [Can I submit my form using websockets?](WebsocketSubmit.md)
20 |
--------------------------------------------------------------------------------
/examples/normalizing/src/normalizePhone.js:
--------------------------------------------------------------------------------
1 | const normalizePhone = (value, previousValue) => {
2 | if (!value) {
3 | return value
4 | }
5 | const onlyNums = value.replace(/[^\d]/g, '')
6 | if (!previousValue || value.length > previousValue.length) {
7 | // typing forward
8 | if (onlyNums.length === 3) {
9 | return onlyNums + '-'
10 | }
11 | if (onlyNums.length === 6) {
12 | return onlyNums.slice(0, 3) + '-' + onlyNums.slice(3) + '-'
13 | }
14 | }
15 | if (onlyNums.length <= 3) {
16 | return onlyNums
17 | }
18 | if (onlyNums.length <= 6) {
19 | return onlyNums.slice(0, 3) + '-' + onlyNums.slice(3)
20 | }
21 | return onlyNums.slice(0, 3) + '-' + onlyNums.slice(3, 6) + '-' + onlyNums.slice(6, 10)
22 | }
23 |
24 | export default normalizePhone
25 |
--------------------------------------------------------------------------------
/src/generateValidator.js:
--------------------------------------------------------------------------------
1 | import plain from './structure/plain'
2 |
3 | const toArray = value => Array.isArray(value) ? value : [ value ]
4 |
5 | const getError = (value, values, validators) => {
6 | for(const validator of toArray(validators)) {
7 | const error = validator(value, values)
8 | if(error) {
9 | return error
10 | }
11 | }
12 | }
13 |
14 | const generateValidator = (validators, { getIn }) =>
15 | values => {
16 | let errors = {}
17 | Object.keys(validators).forEach(name => {
18 | const value = getIn(values, name)
19 | const error = getError(value, values, validators[name])
20 | if(error) {
21 | errors = plain.setIn(errors, name, error)
22 | }
23 | })
24 | return errors
25 | }
26 |
27 | export default generateValidator
28 |
--------------------------------------------------------------------------------
/src/structure/plain/setIn.js:
--------------------------------------------------------------------------------
1 | import { toPath } from 'lodash'
2 |
3 | const setInWithPath = (state, value, path, pathIndex) => {
4 | if (pathIndex >= path.length) {
5 | return value
6 | }
7 |
8 | const first = path[pathIndex]
9 | const next = setInWithPath(state && state[first], value, path, pathIndex + 1)
10 |
11 | if (!state) {
12 | const initialized = isNaN(first) ? {} : []
13 | initialized[first] = next
14 | return initialized
15 | }
16 |
17 | if (Array.isArray(state)) {
18 | const copy = [].concat(state)
19 | copy[first] = next
20 | return copy
21 | }
22 |
23 | return {
24 | ...state,
25 | [first]: next
26 | }
27 | }
28 |
29 | const setIn = (state, field, value) => setInWithPath(state, value, toPath(field), 0)
30 |
31 | export default setIn
32 |
--------------------------------------------------------------------------------
/src/hasError.js:
--------------------------------------------------------------------------------
1 | import plainGetIn from './structure/plain/getIn'
2 |
3 | const getErrorKeys = (name, type) => {
4 | switch (type) {
5 | case 'Field':
6 | return [ name, `${name}._error` ]
7 | case 'FieldArray':
8 | return [ `${name}._error` ]
9 | }
10 | }
11 |
12 | const createHasError = ({ getIn }) => {
13 | const hasError = (field, syncErrors, asyncErrors, submitErrors) => {
14 | if (!syncErrors && !asyncErrors && !submitErrors) {
15 | return false
16 | }
17 |
18 | const name = getIn(field, 'name')
19 | const type = getIn(field, 'type')
20 | return getErrorKeys(name, type).some(key =>
21 | plainGetIn(syncErrors, key) ||
22 | getIn(asyncErrors, key) ||
23 | getIn(submitErrors, key))
24 | }
25 | return hasError
26 | }
27 |
28 | export default createHasError
29 |
--------------------------------------------------------------------------------
/src/structure/immutable/splice.js:
--------------------------------------------------------------------------------
1 | import { List } from 'immutable'
2 |
3 | export default (list, index, removeNum, value) => {
4 | list = List.isList(list) ? list : List()
5 |
6 | if (index < list.count()) {
7 | if (value === undefined && !removeNum) { // inserting undefined
8 | // first insert null and then re-set it to undefined
9 | return list.splice(index, 0, null).set(index, undefined)
10 | }
11 | if (value != null) {
12 | return list.splice(index, removeNum, value) // removing and adding
13 | } else {
14 | return list.splice(index, removeNum) // removing
15 | }
16 | }
17 | if (removeNum) { // trying to remove non-existant item: return original array
18 | return list
19 | }
20 | // trying to add outside of range: just set value
21 | return list.set(index, value)
22 | }
23 |
--------------------------------------------------------------------------------
/src/__tests__/addExpectations.js:
--------------------------------------------------------------------------------
1 | import expect from 'expect'
2 |
3 | /**
4 | * Takes expectations and extends expect with them. Cannot use expect.extends due to the
5 | * asynchronous nature of the tests.
6 | * @param expectations Expectations to add
7 | */
8 | const addExpectations = expectations => {
9 | const decorate = dest => {
10 | const wrap = (value, key) => {
11 | if (typeof value === 'function' && key !== 'actual') {
12 | dest[ key ] = (...params) => decorate(value.apply(dest, params))
13 | }
14 | }
15 | for (let key in dest) {
16 | wrap(dest[ key ], key)
17 | }
18 | for (let key in expectations) {
19 | wrap(expectations[ key ], key)
20 | }
21 | return dest
22 | }
23 | return (...params) => decorate(expect(...params))
24 | }
25 |
26 | export default addExpectations
27 |
--------------------------------------------------------------------------------
/src/util/__tests__/prefixName.spec.js:
--------------------------------------------------------------------------------
1 | import expect from 'expect'
2 | import prefixName from '../prefixName'
3 |
4 | describe('prefixName', () => {
5 | it('should concat sectionPrefix and name', () => {
6 | const context = {
7 | _reduxForm: {
8 | sectionPrefix: 'foo'
9 | }
10 | }
11 | expect(prefixName(context,'bar')).toBe('foo.bar')
12 | })
13 |
14 | it('should ignore empty sectionPrefix', () => {
15 | const context = {
16 | _reduxForm: {
17 | sectionPrefix: undefined
18 | }
19 | }
20 | expect(prefixName(context, 'bar')).toBe('bar')
21 | })
22 |
23 | it('should not prefix array fields', () => {
24 | const context = {
25 | _reduxForm: {
26 | sectionPrefix: 'foo'
27 | }
28 | }
29 | expect(prefixName(context, 'bar.bar[0]')).toBe('bar.bar[0]')
30 | })
31 | })
32 |
--------------------------------------------------------------------------------
/examples/immutable/src/validate.js:
--------------------------------------------------------------------------------
1 | const validate = values => {
2 | // IMPORTANT: values is an Immutable.Map here!
3 | const errors = {}
4 | if (!values.get('username')) {
5 | errors.username = 'Required'
6 | } else if (values.get('username').length > 15) {
7 | errors.username = 'Must be 15 characters or less'
8 | }
9 | if (!values.get('email')) {
10 | errors.email = 'Required'
11 | } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.get('email'))) {
12 | errors.email = 'Invalid email address'
13 | }
14 | if (!values.get('age')) {
15 | errors.age = 'Required'
16 | } else if (isNaN(Number(values.get('age')))) {
17 | errors.age = 'Must be a number'
18 | } else if (Number(values.get('age')) < 18) {
19 | errors.age = 'Sorry, you must be at least 18 years old'
20 | }
21 | return errors
22 | }
23 |
24 | export default validate
25 |
--------------------------------------------------------------------------------
/src/structure/plain/expectations.js:
--------------------------------------------------------------------------------
1 | import expect from 'expect'
2 | import { isObject } from 'lodash'
3 |
4 | const expectations = {
5 | toBeAMap() {
6 | expect.assert(
7 | isObject(this.actual),
8 | 'expected %s to be an object',
9 | this.actual
10 | )
11 | return this
12 | },
13 |
14 | toBeAList() {
15 | expect.assert(
16 | Array.isArray(this.actual),
17 | 'expected %s to be an array',
18 | this.actual
19 | )
20 | return this
21 | },
22 |
23 | toBeSize(size) {
24 | expect.assert(
25 | this.actual && Object.keys(this.actual).length === size,
26 | 'expected %s to contain %s elements',
27 | this.actual,
28 | size
29 | )
30 | return this
31 | },
32 |
33 | toEqualMap(expected) {
34 | return expect(this.actual).toEqual(expected)
35 | }
36 | }
37 |
38 | export default expectations
39 |
--------------------------------------------------------------------------------
/src/structure/plain/splice.js:
--------------------------------------------------------------------------------
1 | const splice = (array, index, removeNum, value) => {
2 | array = array || []
3 |
4 | if (index < array.length) {
5 | if (value === undefined && !removeNum) { // inserting undefined
6 | const copy = [ ...array ]
7 | copy.splice(index, 0, null)
8 | copy[ index ] = undefined
9 | return copy
10 | }
11 | if (value != null) {
12 | const copy = [ ...array ]
13 | copy.splice(index, removeNum, value) // removing and adding
14 | return copy
15 | }
16 | const copy = [ ...array ]
17 | copy.splice(index, removeNum) // removing
18 | return copy
19 | }
20 | if (removeNum) { // trying to remove non-existant item: return original array
21 | return array
22 | }
23 | // trying to add outside of range: just set value
24 | const copy = [ ...array ]
25 | copy[ index ] = value
26 | return copy
27 | }
28 |
29 | export default splice
30 |
--------------------------------------------------------------------------------
/examples/wizard/src/WizardFormFirstPage.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Field, reduxForm } from 'redux-form'
3 | import validate from './validate'
4 | import renderField from './renderField'
5 |
6 | const WizardFormFirstPage = (props) => {
7 | const { handleSubmit } = props
8 | return (
9 |
16 | )
17 | }
18 |
19 | export default reduxForm({
20 | form: 'wizard', // <------ same form name
21 | destroyOnUnmount: false, // <------ preserve form data
22 | forceUnregisterOnUnmount: true, // <------ unregister fields on unmount
23 | validate
24 | })(WizardFormFirstPage)
25 |
--------------------------------------------------------------------------------
/src/structure/immutable/index.js:
--------------------------------------------------------------------------------
1 | import { Map, Iterable, List, fromJS } from 'immutable'
2 | import { toPath } from 'lodash'
3 | import deepEqual from './deepEqual'
4 | import setIn from './setIn'
5 | import splice from './splice'
6 | import plainGetIn from '../plain/getIn'
7 |
8 | const structure = {
9 | empty: Map(),
10 | emptyList: List(),
11 | getIn: (state, field) =>
12 | Map.isMap(state) || List.isList(state) ? state.getIn(toPath(field)) : plainGetIn(state, field),
13 | setIn,
14 | deepEqual,
15 | deleteIn: (state, field) => state.deleteIn(toPath(field)),
16 | fromJS: jsValue => fromJS(jsValue, (key, value) =>
17 | Iterable.isIndexed(value) ? value.toList() : value.toMap()),
18 | size: list => list ? list.size : 0,
19 | some: (iterable, callback) => Iterable.isIterable(iterable) ? iterable.some(callback) : false,
20 | splice,
21 | toJS: value => Iterable.isIterable(value) ? value.toJS() : value
22 | }
23 |
24 | export default structure
25 |
--------------------------------------------------------------------------------
/src/__tests__/reducer.clearAsyncError.spec.js:
--------------------------------------------------------------------------------
1 | import { clearAsyncError } from '../actions'
2 |
3 | const describeClearAsyncError = (reducer, expect, { fromJS }) => () => {
4 | it('should do nothing on clear submit with no previous state', () => {
5 | const state = reducer(undefined, clearAsyncError('foo'))
6 | expect(state)
7 | .toEqualMap({
8 | foo: {}
9 | })
10 | })
11 |
12 | it('should clear async errors with previous state', () => {
13 | const state = reducer(fromJS({
14 | myForm: {
15 | asyncErrors: {
16 | foo: 'some validation message here',
17 | baar: 'second validation message'
18 | }
19 | }
20 | }), clearAsyncError('myForm', 'foo'))
21 | expect(state)
22 | .toEqualMap({
23 | myForm: {
24 | asyncErrors: {
25 | baar: 'second validation message'
26 | }
27 | }
28 | })
29 | })
30 | }
31 |
32 | export default describeClearAsyncError
33 |
--------------------------------------------------------------------------------
/src/formValueSelector.js:
--------------------------------------------------------------------------------
1 | import invariant from 'invariant'
2 | import plain from './structure/plain'
3 |
4 | const createFormValueSelector =
5 | ({ getIn }) =>
6 | (form, getFormState = state => getIn(state, 'form')) => {
7 | invariant(form, 'Form value must be specified')
8 | return (state, ...fields) => {
9 | invariant(fields.length, 'No fields specified')
10 | return fields.length === 1 ?
11 | // only selecting one field, so return its value
12 | getIn(getFormState(state), `${form}.values.${fields[ 0 ]}`) :
13 | // selecting many fields, so return an object of field values
14 | fields.reduce((accumulator, field) => {
15 | const value = getIn(getFormState(state), `${form}.values.${field}`)
16 | return value === undefined ?
17 | accumulator :
18 | plain.setIn(accumulator, field, value)
19 | }, {})
20 | }
21 | }
22 |
23 | export default createFormValueSelector
24 |
25 |
--------------------------------------------------------------------------------
/src/immutable.js:
--------------------------------------------------------------------------------
1 | import createAll from './createAll'
2 | import immutable from './structure/immutable'
3 |
4 | export const {
5 | actionTypes,
6 | arrayInsert,
7 | arrayMove,
8 | arrayPop,
9 | arrayPush,
10 | arrayRemove,
11 | arrayRemoveAll,
12 | arrayShift,
13 | arraySplice,
14 | arraySwap,
15 | arrayUnshift,
16 | autofill,
17 | blur,
18 | change,
19 | destroy,
20 | Field,
21 | Fields,
22 | FieldArray,
23 | Form,
24 | FormSection,
25 | focus,
26 | formValueSelector,
27 | getFormValues,
28 | initialize,
29 | isDirty,
30 | isInvalid,
31 | isPristine,
32 | isValid,
33 | isSubmitting,
34 | hasSubmitSucceeded,
35 | hasSubmitFailed,
36 | propTypes,
37 | reducer,
38 | reduxForm,
39 | reset,
40 | setSubmitFailed,
41 | setSubmitSucceeded,
42 | startAsyncValidation,
43 | startSubmit,
44 | stopAsyncValidation,
45 | stopSubmit,
46 | submit,
47 | SubmissionError,
48 | touch,
49 | untouch,
50 | values
51 | } = createAll(immutable)
52 |
--------------------------------------------------------------------------------
/src/deleteInWithCleanUp.js:
--------------------------------------------------------------------------------
1 | import { toPath } from 'lodash'
2 |
3 | const createDeleteInWithCleanUp = ({ deepEqual, empty, getIn, deleteIn, setIn }) => {
4 |
5 | const deleteInWithCleanUp = (state, path) => {
6 | if (path[ path.length - 1 ] === ']') { // array path
7 | const pathTokens = toPath(path)
8 | pathTokens.pop()
9 | const parent = getIn(state, pathTokens.join('.'))
10 | return parent ? setIn(state, path, undefined) : state
11 | }
12 | const result = deleteIn(state, path)
13 | const dotIndex = path.lastIndexOf('.')
14 | if (dotIndex > 0) {
15 | const parentPath = path.substring(0, dotIndex)
16 | if (parentPath[ parentPath.length - 1 ] !== ']') {
17 | const parent = getIn(result, parentPath)
18 | if (deepEqual(parent, empty)) {
19 | return deleteInWithCleanUp(result, parentPath)
20 | }
21 | }
22 | }
23 | return result
24 | }
25 |
26 | return deleteInWithCleanUp
27 | }
28 |
29 | export default createDeleteInWithCleanUp
30 |
--------------------------------------------------------------------------------
/src/util/__tests__/eventMocks.spec.js:
--------------------------------------------------------------------------------
1 | import expect from 'expect'
2 | import * as mocks from '../eventMocks'
3 |
4 | describe('#eventMocks', () => {
5 | it('should create a mock with identity functions', () => {
6 | const event = mocks.valueMock('value')
7 |
8 | expect(event.preventDefault).toBeA('function')
9 | expect(event.stopPropagation).toBeA('function')
10 | expect(event.preventDefault('id')).toEqual('id')
11 | expect(event.stopPropagation('id')).toEqual('id')
12 | })
13 |
14 | it('should create a value mock', () => {
15 | const event = mocks.valueMock('value')
16 |
17 | expect(event.target.value).toEqual('value')
18 | })
19 |
20 | it('should create a drag start mock', () => {
21 | const fn = id => id
22 | const event = mocks.dragStartMock(fn)
23 |
24 | expect(event.dataTransfer.setData).toBe(fn)
25 | })
26 |
27 | it('should create a drop mock', () => {
28 | const fn = id => id
29 | const event = mocks.dropMock(fn)
30 |
31 | expect(event.dataTransfer.getData).toBe(fn)
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/examples/remoteSubmit/src/RemoteSubmit.md:
--------------------------------------------------------------------------------
1 | # Remote Submit Example
2 |
3 | This example demonstrates how a form may be submitted by dispatching a `SUBMIT` action from an
4 | unrelated component or middleware.
5 |
6 | The "Submit" button you see here is not connected to the form component in any way; it only
7 | dispatches an action via Redux.
8 |
9 | Notice that for this to work, the submit function must be given to the form component via either
10 | a config value passed to `reduxForm()` or via a prop.
11 |
12 | ## Running this example locally
13 |
14 | To run this example locally on your machine clone the `redux-form` repository,
15 | then `cd redux-form` to change to the repo directory, and run `npm install`.
16 |
17 | Then run `npm run example:remoteSubmit` or manually run the
18 | following commands:
19 | ```
20 | cd ./examples/remoteSubmit
21 | npm install
22 | npm start
23 | ```
24 |
25 | ## How to use the form below:
26 |
27 | * Usernames that will pass validation: `john`, `paul`, `george`, or `ringo`.
28 | * Valid password for all users: `redux-form`.
29 |
30 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import createAll from './createAll'
2 | import plain from './structure/plain'
3 |
4 | export const {
5 | actionTypes,
6 | arrayInsert,
7 | arrayMove,
8 | arrayPop,
9 | arrayPush,
10 | arrayRemove,
11 | arrayRemoveAll,
12 | arrayShift,
13 | arraySplice,
14 | arraySwap,
15 | arrayUnshift,
16 | blur,
17 | autofill,
18 | change,
19 | destroy,
20 | Field,
21 | Fields,
22 | FieldArray,
23 | Form,
24 | FormSection,
25 | focus,
26 | formValueSelector,
27 | getFormValues,
28 | getFormSyncErrors,
29 | getFormSubmitErrors,
30 | initialize,
31 | isDirty,
32 | isInvalid,
33 | isPristine,
34 | isValid,
35 | isSubmitting,
36 | hasSubmitSucceeded,
37 | hasSubmitFailed,
38 | propTypes,
39 | reducer,
40 | reduxForm,
41 | registerField,
42 | reset,
43 | setSubmitFailed,
44 | setSubmitSucceeded,
45 | startAsyncValidation,
46 | startSubmit,
47 | stopAsyncValidation,
48 | stopSubmit,
49 | submit,
50 | SubmissionError,
51 | touch,
52 | unregisterField,
53 | untouch,
54 | values
55 | } = createAll(plain)
56 |
--------------------------------------------------------------------------------
/docs/api/SubmissionError.md:
--------------------------------------------------------------------------------
1 | # `SubmissionError`
2 |
3 | [`View source on GitHub`](https://github.com/erikras/redux-form/blob/master/src/SubmissionError.js)
4 |
5 | A throwable error that is used to return submit validation errors from `onSubmit`. The purpose
6 | being to distinguish promise rejection because of validation errors from promise rejection because
7 | of AJAX I/O problems or other server errors.
8 |
9 | ## Importing
10 |
11 | ```javascript
12 | var SubmissionError = require('redux-form').SubmissionError; // ES5
13 | ```
14 | ```javascript
15 | import { SubmissionError } from 'redux-form'; // ES6
16 | ```
17 |
18 | ## Usage
19 |
20 | ```js
21 |
22 | ajax.send(values) // however you send data to your server...
23 | .catch(error => {
24 | // how you pass server-side validation errors back is up to you
25 | if(error.validationErrors) {
26 | throw new SubmissionError(error.validationErrors)
27 | } else {
28 | // what you do about other communication errors is up to you
29 | }
30 | })
31 | }/>
32 | ```
33 |
34 |
--------------------------------------------------------------------------------
/examples/wizard/src/Wizard.md:
--------------------------------------------------------------------------------
1 | # Wizard Form
2 |
3 | One common UI design pattern is to separate a single form out into sepearate pages of inputs,
4 | commonly known as a Wizard. There are several ways that this could be accomplished using
5 | `redux-form`, but the simplest and recommended way is to follow these instructions:
6 |
7 | * Connect each page with `reduxForm()` to the same form name.
8 | * Specify the `destroyOnUnmount: false` flag to preserve form data across form component unmounts.
9 | * You may specify sync validation for the entire form
10 | * Use `onSubmit` to transition forward to the next page; this forces validation to run.
11 |
12 | Things that are up to you to implement:
13 |
14 | * Call `props.destroy()` manually after a successful submit.
15 |
16 | ## Running this example locally
17 |
18 | To run this example locally on your machine clone the `redux-form` repository,
19 | then `cd redux-form` to change to the repo directory, and run `npm install`.
20 |
21 | Then run `npm run example:wizard` or manually run the
22 | following commands:
23 | ```
24 | cd ./examples/wizard
25 | npm install
26 | npm start
27 | ```
28 |
29 |
--------------------------------------------------------------------------------
/docs/faq/ReactNative.md:
--------------------------------------------------------------------------------
1 | # Does `redux-form` work with React Native?
2 |
3 | Yes, it does.
4 |
5 | #### If your are using all `react-native@18+`, `redux@4+` and `npm@3+`
6 |
7 | Just import it as usual:
8 |
9 | ```javascript
10 | import {reduxForm} from 'redux-form';
11 | ```
12 |
13 | #### Else
14 |
15 | All you have to do is use:
16 |
17 | ```javascript
18 | import {reduxForm} from 'redux-form/native';
19 | ```
20 | instead of
21 | ```javascript
22 | import {reduxForm} from 'redux-form';
23 | ```
24 |
25 | #### Note:
26 |
27 | `react-redux/native` is deprecated in `react-redux@4+`, it only appears in `react-redux@3`
28 |
29 | #### See Also:
30 |
31 | 1. [[react-native] Depend on npm "react" instead of shipping with a vendored copy #2985](https://github.com/facebook/react-native/issues/2985)
32 | 2. [[react-redux] Update React Native installation instructions #236](https://github.com/rackt/react-redux/issues/236)
33 | 3. [Until React Native works on top of React instead of shipping a fork of React, you’ll need to keep using React Redux 3.x branch and documentation.](https://github.com/erikras/redux-form/issues/473#issuecomment-167690524)
34 |
--------------------------------------------------------------------------------
/examples/simple/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'babel-polyfill',
8 | 'eventsource-polyfill', // necessary for hot reloading with IE
9 | 'webpack-hot-middleware/client',
10 | './src/index'
11 | ],
12 | output: {
13 | path: path.join(__dirname, 'dist'),
14 | filename: 'bundle.js',
15 | publicPath: '/dist/'
16 | },
17 | plugins: [
18 | new webpack.optimize.OccurenceOrderPlugin(),
19 | new webpack.HotModuleReplacementPlugin()
20 | ],
21 | resolve: {
22 | modulesDirectories: [
23 | 'src',
24 | 'node_modules'
25 | ],
26 | extensions: [ '', '.json', '.js' ]
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.jsx?/,
32 | loaders: [ 'babel', 'eslint' ],
33 | include: path.join(__dirname, 'src')
34 | },
35 | {
36 | test: /\.json$/,
37 | loader: 'json-loader'
38 | },
39 | {
40 | test: /\.md/,
41 | loaders: [ "html-loader", "markdown-loader" ]
42 | }
43 | ]
44 | }
45 | };
--------------------------------------------------------------------------------
/examples/wizard/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'babel-polyfill',
8 | 'eventsource-polyfill', // necessary for hot reloading with IE
9 | 'webpack-hot-middleware/client',
10 | './src/index'
11 | ],
12 | output: {
13 | path: path.join(__dirname, 'dist'),
14 | filename: 'bundle.js',
15 | publicPath: '/dist/'
16 | },
17 | plugins: [
18 | new webpack.optimize.OccurenceOrderPlugin(),
19 | new webpack.HotModuleReplacementPlugin()
20 | ],
21 | resolve: {
22 | modulesDirectories: [
23 | 'src',
24 | 'node_modules'
25 | ],
26 | extensions: [ '', '.json', '.js' ]
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.jsx?/,
32 | loaders: [ 'babel', 'eslint' ],
33 | include: path.join(__dirname, 'src')
34 | },
35 | {
36 | test: /\.json$/,
37 | loader: 'json-loader'
38 | },
39 | {
40 | test: /\.md/,
41 | loaders: [ "html-loader", "markdown-loader" ]
42 | }
43 | ]
44 | }
45 | };
--------------------------------------------------------------------------------
/src/__tests__/reducer.startSubmit.spec.js:
--------------------------------------------------------------------------------
1 | import { startSubmit } from '../actions'
2 |
3 | const describeStartSubmit = (reducer, expect, { fromJS }) => () => {
4 | it('should set submitting on startSubmit', () => {
5 | const state = reducer(fromJS({
6 | foo: {
7 | doesnt: 'matter',
8 | should: 'notchange'
9 | }
10 | }), startSubmit('foo'))
11 | expect(state)
12 | .toEqualMap({
13 | foo: {
14 | doesnt: 'matter',
15 | should: 'notchange',
16 | submitting: true
17 | }
18 | })
19 | })
20 |
21 | it('should set submitting on startSubmit, and NOT reset submitFailed', () => {
22 | const state = reducer(fromJS({
23 | foo: {
24 | doesnt: 'matter',
25 | should: 'notchange',
26 | submitFailed: true
27 | }
28 | }), startSubmit('foo'))
29 | expect(state)
30 | .toEqualMap({
31 | foo: {
32 | doesnt: 'matter',
33 | should: 'notchange',
34 | submitting: true,
35 | submitFailed: true
36 | }
37 | })
38 | })
39 | }
40 |
41 | export default describeStartSubmit
42 |
--------------------------------------------------------------------------------
/src/events/__tests__/silenceEvent.spec.js:
--------------------------------------------------------------------------------
1 | import expect, { createSpy } from 'expect'
2 | import { noop } from 'lodash'
3 | import silenceEvent from '../silenceEvent'
4 |
5 | describe('silenceEvent', () => {
6 | it('should return false if not an event', () => {
7 | expect(silenceEvent(undefined)).toBe(false)
8 | expect(silenceEvent(null)).toBe(false)
9 | expect(silenceEvent(true)).toBe(false)
10 | expect(silenceEvent(42)).toBe(false)
11 | expect(silenceEvent({})).toBe(false)
12 | expect(silenceEvent([])).toBe(false)
13 | expect(silenceEvent(noop)).toBe(false)
14 | })
15 |
16 | it('should return true if an event', () => {
17 | expect(silenceEvent({
18 | preventDefault: noop,
19 | stopPropagation: noop
20 | })).toBe(true)
21 | })
22 |
23 | it('should call preventDefault and stopPropagation', () => {
24 | const preventDefault = createSpy()
25 | const stopPropagation = createSpy()
26 |
27 | silenceEvent({
28 | preventDefault,
29 | stopPropagation
30 | })
31 | expect(preventDefault).toHaveBeenCalled()
32 | expect(stopPropagation).toNotHaveBeenCalled()
33 | })
34 | })
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Erik Rasmussen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/examples/fieldArrays/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'babel-polyfill',
8 | 'eventsource-polyfill', // necessary for hot reloading with IE
9 | 'webpack-hot-middleware/client',
10 | './src/index'
11 | ],
12 | output: {
13 | path: path.join(__dirname, 'dist'),
14 | filename: 'bundle.js',
15 | publicPath: '/dist/'
16 | },
17 | plugins: [
18 | new webpack.optimize.OccurenceOrderPlugin(),
19 | new webpack.HotModuleReplacementPlugin()
20 | ],
21 | resolve: {
22 | modulesDirectories: [
23 | 'src',
24 | 'node_modules'
25 | ],
26 | extensions: [ '', '.json', '.js' ]
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.jsx?/,
32 | loaders: [ 'babel', 'eslint' ],
33 | include: path.join(__dirname, 'src')
34 | },
35 | {
36 | test: /\.json$/,
37 | loader: 'json-loader'
38 | },
39 | {
40 | test: /\.md/,
41 | loaders: [ "html-loader", "markdown-loader" ]
42 | }
43 | ]
44 | }
45 | };
--------------------------------------------------------------------------------
/examples/immutable/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'babel-polyfill',
8 | 'eventsource-polyfill', // necessary for hot reloading with IE
9 | 'webpack-hot-middleware/client',
10 | './src/index'
11 | ],
12 | output: {
13 | path: path.join(__dirname, 'dist'),
14 | filename: 'bundle.js',
15 | publicPath: '/dist/'
16 | },
17 | plugins: [
18 | new webpack.optimize.OccurenceOrderPlugin(),
19 | new webpack.HotModuleReplacementPlugin()
20 | ],
21 | resolve: {
22 | modulesDirectories: [
23 | 'src',
24 | 'node_modules'
25 | ],
26 | extensions: [ '', '.json', '.js' ]
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.jsx?/,
32 | loaders: [ 'babel', 'eslint' ],
33 | include: path.join(__dirname, 'src')
34 | },
35 | {
36 | test: /\.json$/,
37 | loader: 'json-loader'
38 | },
39 | {
40 | test: /\.md/,
41 | loaders: [ "html-loader", "markdown-loader" ]
42 | }
43 | ]
44 | }
45 | };
--------------------------------------------------------------------------------
/examples/material-ui/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'babel-polyfill',
8 | 'eventsource-polyfill', // necessary for hot reloading with IE
9 | 'webpack-hot-middleware/client',
10 | './src/index'
11 | ],
12 | output: {
13 | path: path.join(__dirname, 'dist'),
14 | filename: 'bundle.js',
15 | publicPath: '/dist/'
16 | },
17 | plugins: [
18 | new webpack.optimize.OccurenceOrderPlugin(),
19 | new webpack.HotModuleReplacementPlugin()
20 | ],
21 | resolve: {
22 | modulesDirectories: [
23 | 'src',
24 | 'node_modules'
25 | ],
26 | extensions: [ '', '.json', '.js' ]
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.jsx?/,
32 | loaders: [ 'babel', 'eslint' ],
33 | include: path.join(__dirname, 'src')
34 | },
35 | {
36 | test: /\.json$/,
37 | loader: 'json-loader'
38 | },
39 | {
40 | test: /\.md/,
41 | loaders: [ "html-loader", "markdown-loader" ]
42 | }
43 | ]
44 | }
45 | };
--------------------------------------------------------------------------------
/examples/normalizing/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'babel-polyfill',
8 | 'eventsource-polyfill', // necessary for hot reloading with IE
9 | 'webpack-hot-middleware/client',
10 | './src/index'
11 | ],
12 | output: {
13 | path: path.join(__dirname, 'dist'),
14 | filename: 'bundle.js',
15 | publicPath: '/dist/'
16 | },
17 | plugins: [
18 | new webpack.optimize.OccurenceOrderPlugin(),
19 | new webpack.HotModuleReplacementPlugin()
20 | ],
21 | resolve: {
22 | modulesDirectories: [
23 | 'src',
24 | 'node_modules'
25 | ],
26 | extensions: [ '', '.json', '.js' ]
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.jsx?/,
32 | loaders: [ 'babel', 'eslint' ],
33 | include: path.join(__dirname, 'src')
34 | },
35 | {
36 | test: /\.json$/,
37 | loader: 'json-loader'
38 | },
39 | {
40 | test: /\.md/,
41 | loaders: [ "html-loader", "markdown-loader" ]
42 | }
43 | ]
44 | }
45 | };
--------------------------------------------------------------------------------
/examples/asyncValidation/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'babel-polyfill',
8 | 'eventsource-polyfill', // necessary for hot reloading with IE
9 | 'webpack-hot-middleware/client',
10 | './src/index'
11 | ],
12 | output: {
13 | path: path.join(__dirname, 'dist'),
14 | filename: 'bundle.js',
15 | publicPath: '/dist/'
16 | },
17 | plugins: [
18 | new webpack.optimize.OccurenceOrderPlugin(),
19 | new webpack.HotModuleReplacementPlugin()
20 | ],
21 | resolve: {
22 | modulesDirectories: [
23 | 'src',
24 | 'node_modules'
25 | ],
26 | extensions: [ '', '.json', '.js' ]
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.jsx?/,
32 | loaders: [ 'babel', 'eslint' ],
33 | include: path.join(__dirname, 'src')
34 | },
35 | {
36 | test: /\.json$/,
37 | loader: 'json-loader'
38 | },
39 | {
40 | test: /\.md/,
41 | loaders: [ "html-loader", "markdown-loader" ]
42 | }
43 | ]
44 | }
45 | };
--------------------------------------------------------------------------------
/examples/remoteSubmit/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'babel-polyfill',
8 | 'eventsource-polyfill', // necessary for hot reloading with IE
9 | 'webpack-hot-middleware/client',
10 | './src/index'
11 | ],
12 | output: {
13 | path: path.join(__dirname, 'dist'),
14 | filename: 'bundle.js',
15 | publicPath: '/dist/'
16 | },
17 | plugins: [
18 | new webpack.optimize.OccurenceOrderPlugin(),
19 | new webpack.HotModuleReplacementPlugin()
20 | ],
21 | resolve: {
22 | modulesDirectories: [
23 | 'src',
24 | 'node_modules'
25 | ],
26 | extensions: [ '', '.json', '.js' ]
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.jsx?/,
32 | loaders: [ 'babel', 'eslint' ],
33 | include: path.join(__dirname, 'src')
34 | },
35 | {
36 | test: /\.json$/,
37 | loader: 'json-loader'
38 | },
39 | {
40 | test: /\.md/,
41 | loaders: [ "html-loader", "markdown-loader" ]
42 | }
43 | ]
44 | }
45 | };
--------------------------------------------------------------------------------
/examples/submitValidation/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'babel-polyfill',
8 | 'eventsource-polyfill', // necessary for hot reloading with IE
9 | 'webpack-hot-middleware/client',
10 | './src/index'
11 | ],
12 | output: {
13 | path: path.join(__dirname, 'dist'),
14 | filename: 'bundle.js',
15 | publicPath: '/dist/'
16 | },
17 | plugins: [
18 | new webpack.optimize.OccurenceOrderPlugin(),
19 | new webpack.HotModuleReplacementPlugin()
20 | ],
21 | resolve: {
22 | modulesDirectories: [
23 | 'src',
24 | 'node_modules'
25 | ],
26 | extensions: [ '', '.json', '.js' ]
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.jsx?/,
32 | loaders: [ 'babel', 'eslint' ],
33 | include: path.join(__dirname, 'src')
34 | },
35 | {
36 | test: /\.json$/,
37 | loader: 'json-loader'
38 | },
39 | {
40 | test: /\.md/,
41 | loaders: [ "html-loader", "markdown-loader" ]
42 | }
43 | ]
44 | }
45 | };
--------------------------------------------------------------------------------
/examples/syncValidation/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'babel-polyfill',
8 | 'eventsource-polyfill', // necessary for hot reloading with IE
9 | 'webpack-hot-middleware/client',
10 | './src/index'
11 | ],
12 | output: {
13 | path: path.join(__dirname, 'dist'),
14 | filename: 'bundle.js',
15 | publicPath: '/dist/'
16 | },
17 | plugins: [
18 | new webpack.optimize.OccurenceOrderPlugin(),
19 | new webpack.HotModuleReplacementPlugin()
20 | ],
21 | resolve: {
22 | modulesDirectories: [
23 | 'src',
24 | 'node_modules'
25 | ],
26 | extensions: [ '', '.json', '.js' ]
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.jsx?/,
32 | loaders: [ 'babel', 'eslint' ],
33 | include: path.join(__dirname, 'src')
34 | },
35 | {
36 | test: /\.json$/,
37 | loader: 'json-loader'
38 | },
39 | {
40 | test: /\.md/,
41 | loaders: [ "html-loader", "markdown-loader" ]
42 | }
43 | ]
44 | }
45 | };
--------------------------------------------------------------------------------
/examples/fieldLevelValidation/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'babel-polyfill',
8 | 'eventsource-polyfill', // necessary for hot reloading with IE
9 | 'webpack-hot-middleware/client',
10 | './src/index'
11 | ],
12 | output: {
13 | path: path.join(__dirname, 'dist'),
14 | filename: 'bundle.js',
15 | publicPath: '/dist/'
16 | },
17 | plugins: [
18 | new webpack.optimize.OccurenceOrderPlugin(),
19 | new webpack.HotModuleReplacementPlugin()
20 | ],
21 | resolve: {
22 | modulesDirectories: [
23 | 'src',
24 | 'node_modules'
25 | ],
26 | extensions: [ '', '.json', '.js' ]
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.jsx?/,
32 | loaders: [ 'babel', 'eslint' ],
33 | include: path.join(__dirname, 'src')
34 | },
35 | {
36 | test: /\.json$/,
37 | loader: 'json-loader'
38 | },
39 | {
40 | test: /\.md/,
41 | loaders: [ "html-loader", "markdown-loader" ]
42 | }
43 | ]
44 | }
45 | };
--------------------------------------------------------------------------------
/examples/initializeFromState/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'babel-polyfill',
8 | 'eventsource-polyfill', // necessary for hot reloading with IE
9 | 'webpack-hot-middleware/client',
10 | './src/index'
11 | ],
12 | output: {
13 | path: path.join(__dirname, 'dist'),
14 | filename: 'bundle.js',
15 | publicPath: '/dist/'
16 | },
17 | plugins: [
18 | new webpack.optimize.OccurenceOrderPlugin(),
19 | new webpack.HotModuleReplacementPlugin()
20 | ],
21 | resolve: {
22 | modulesDirectories: [
23 | 'src',
24 | 'node_modules'
25 | ],
26 | extensions: [ '', '.json', '.js' ]
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.jsx?/,
32 | loaders: [ 'babel', 'eslint' ],
33 | include: path.join(__dirname, 'src')
34 | },
35 | {
36 | test: /\.json$/,
37 | loader: 'json-loader'
38 | },
39 | {
40 | test: /\.md/,
41 | loaders: [ "html-loader", "markdown-loader" ]
42 | }
43 | ]
44 | }
45 | };
--------------------------------------------------------------------------------
/examples/selectingFormValues/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'babel-polyfill',
8 | 'eventsource-polyfill', // necessary for hot reloading with IE
9 | 'webpack-hot-middleware/client',
10 | './src/index'
11 | ],
12 | output: {
13 | path: path.join(__dirname, 'dist'),
14 | filename: 'bundle.js',
15 | publicPath: '/dist/'
16 | },
17 | plugins: [
18 | new webpack.optimize.OccurenceOrderPlugin(),
19 | new webpack.HotModuleReplacementPlugin()
20 | ],
21 | resolve: {
22 | modulesDirectories: [
23 | 'src',
24 | 'node_modules'
25 | ],
26 | extensions: [ '', '.json', '.js' ]
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.jsx?/,
32 | loaders: [ 'babel', 'eslint' ],
33 | include: path.join(__dirname, 'src')
34 | },
35 | {
36 | test: /\.json$/,
37 | loader: 'json-loader'
38 | },
39 | {
40 | test: /\.md/,
41 | loaders: [ "html-loader", "markdown-loader" ]
42 | }
43 | ]
44 | }
45 | };
--------------------------------------------------------------------------------
/src/events/getValue.js:
--------------------------------------------------------------------------------
1 | import isEvent from './isEvent'
2 |
3 | const getSelectedValues = options => {
4 | const result = []
5 | if (options) {
6 | for (let index = 0; index < options.length; index++) {
7 | const option = options[ index ]
8 | if (option.selected) {
9 | result.push(option.value)
10 | }
11 | }
12 | }
13 | return result
14 | }
15 |
16 | const getValue = (event, isReactNative) => {
17 | if (isEvent(event)) {
18 | if (!isReactNative && event.nativeEvent && event.nativeEvent.text !== undefined) {
19 | return event.nativeEvent.text
20 | }
21 | if (isReactNative && event.nativeEvent !== undefined) {
22 | return event.nativeEvent.text
23 | }
24 | const { target: { type, value, checked, files }, dataTransfer } = event
25 | if (type === 'checkbox') {
26 | return checked
27 | }
28 | if (type === 'file') {
29 | return files || dataTransfer && dataTransfer.files
30 | }
31 | if (type === 'select-multiple') {
32 | return getSelectedValues(event.target.options)
33 | }
34 | return value
35 | }
36 | return event
37 | }
38 |
39 | export default getValue
40 |
--------------------------------------------------------------------------------
/examples/remoteSubmit/src/RemoteSubmitForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Field, reduxForm } from 'redux-form'
3 | import submit from './submit'
4 |
5 | const renderField = ({ input, label, type, meta: { touched, error } }) => (
6 |
7 |
8 |
9 |
10 | {touched && error && {error}}
11 |
12 |
13 | )
14 |
15 | const RemoteSubmitForm = (props) => {
16 | const { error, handleSubmit } = props
17 | return (
18 |
26 | )
27 | }
28 |
29 | export default reduxForm({
30 | form: 'remoteSubmit', // a unique identifier for this form
31 | onSubmit: submit // submit function must be passed to onSubmit
32 | })(RemoteSubmitForm)
33 |
--------------------------------------------------------------------------------
/src/events/__tests__/isEvent.spec.js:
--------------------------------------------------------------------------------
1 | import expect from 'expect'
2 | import { noop } from 'lodash'
3 | import isEvent from '../isEvent'
4 |
5 | describe('isEvent', () => {
6 | it('should return false if event is undefined', () => {
7 | expect(isEvent()).toBe(false)
8 | })
9 |
10 | it('should return false if event is null', () => {
11 | expect(isEvent(null)).toBe(false)
12 | })
13 |
14 | it('should return false if event is not an object', () => {
15 | expect(isEvent(42)).toBe(false)
16 | expect(isEvent(true)).toBe(false)
17 | expect(isEvent(false)).toBe(false)
18 | expect(isEvent('not an event')).toBe(false)
19 | })
20 |
21 | it('should return false if event has no stopPropagation', () => {
22 | expect(isEvent({
23 | preventDefault: noop
24 | })).toBe(false)
25 | })
26 |
27 | it('should return false if event has no preventDefault', () => {
28 | expect(isEvent({
29 | stopPropagation: noop
30 | })).toBe(false)
31 | })
32 |
33 | it('should return true if event has stopPropagation, and preventDefault', () => {
34 | expect(isEvent({
35 | stopPropagation: noop,
36 | preventDefault: noop
37 | })).toBe(true)
38 | })
39 | })
40 |
--------------------------------------------------------------------------------
/examples/submitValidation/src/SubmitValidationForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Field, reduxForm } from 'redux-form'
3 | import submit from './submit'
4 |
5 | const renderField = ({ input, label, type, meta: { touched, error } }) => (
6 |
7 |
8 |
9 |
10 | {touched && error && {error}}
11 |
12 |
13 | )
14 |
15 | const SubmitValidationForm = (props) => {
16 | const { error, handleSubmit, pristine, reset, submitting } = props
17 | return (
18 |
27 | )
28 | }
29 |
30 | export default reduxForm({
31 | form: 'submitValidation' // a unique identifier for this form
32 | })(SubmitValidationForm)
33 |
--------------------------------------------------------------------------------
/examples/simple/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'babel-polyfill',
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/dist/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | NODE_ENV: JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | resolve: {
29 | modulesDirectories: [
30 | 'src',
31 | 'node_modules'
32 | ],
33 | extensions: [ '', '.json', '.js' ]
34 | },
35 | module: {
36 | loaders: [
37 | {
38 | test: /\.js$/,
39 | loaders: [ 'babel' ],
40 | include: path.join(__dirname, 'src')
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json-loader'
45 | },
46 | {
47 | test: /\.md/,
48 | loaders: [ "html-loader", "markdown-loader" ]
49 | }
50 | ]
51 | }
52 | };
--------------------------------------------------------------------------------
/examples/wizard/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'babel-polyfill',
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/dist/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | NODE_ENV: JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | resolve: {
29 | modulesDirectories: [
30 | 'src',
31 | 'node_modules'
32 | ],
33 | extensions: [ '', '.json', '.js' ]
34 | },
35 | module: {
36 | loaders: [
37 | {
38 | test: /\.js$/,
39 | loaders: [ 'babel' ],
40 | include: path.join(__dirname, 'src')
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json-loader'
45 | },
46 | {
47 | test: /\.md/,
48 | loaders: [ "html-loader", "markdown-loader" ]
49 | }
50 | ]
51 | }
52 | };
--------------------------------------------------------------------------------
/examples/fieldArrays/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'babel-polyfill',
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/dist/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | NODE_ENV: JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | resolve: {
29 | modulesDirectories: [
30 | 'src',
31 | 'node_modules'
32 | ],
33 | extensions: [ '', '.json', '.js' ]
34 | },
35 | module: {
36 | loaders: [
37 | {
38 | test: /\.js$/,
39 | loaders: [ 'babel' ],
40 | include: path.join(__dirname, 'src')
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json-loader'
45 | },
46 | {
47 | test: /\.md/,
48 | loaders: [ "html-loader", "markdown-loader" ]
49 | }
50 | ]
51 | }
52 | };
--------------------------------------------------------------------------------
/examples/immutable/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'babel-polyfill',
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/dist/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | NODE_ENV: JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | resolve: {
29 | modulesDirectories: [
30 | 'src',
31 | 'node_modules'
32 | ],
33 | extensions: [ '', '.json', '.js' ]
34 | },
35 | module: {
36 | loaders: [
37 | {
38 | test: /\.js$/,
39 | loaders: [ 'babel' ],
40 | include: path.join(__dirname, 'src')
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json-loader'
45 | },
46 | {
47 | test: /\.md/,
48 | loaders: [ "html-loader", "markdown-loader" ]
49 | }
50 | ]
51 | }
52 | };
--------------------------------------------------------------------------------
/examples/material-ui/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'babel-polyfill',
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/dist/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | NODE_ENV: JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | resolve: {
29 | modulesDirectories: [
30 | 'src',
31 | 'node_modules'
32 | ],
33 | extensions: [ '', '.json', '.js' ]
34 | },
35 | module: {
36 | loaders: [
37 | {
38 | test: /\.js$/,
39 | loaders: [ 'babel' ],
40 | include: path.join(__dirname, 'src')
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json-loader'
45 | },
46 | {
47 | test: /\.md/,
48 | loaders: [ "html-loader", "markdown-loader" ]
49 | }
50 | ]
51 | }
52 | };
--------------------------------------------------------------------------------
/examples/normalizing/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'babel-polyfill',
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/dist/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | NODE_ENV: JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | resolve: {
29 | modulesDirectories: [
30 | 'src',
31 | 'node_modules'
32 | ],
33 | extensions: [ '', '.json', '.js' ]
34 | },
35 | module: {
36 | loaders: [
37 | {
38 | test: /\.js$/,
39 | loaders: [ 'babel' ],
40 | include: path.join(__dirname, 'src')
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json-loader'
45 | },
46 | {
47 | test: /\.md/,
48 | loaders: [ "html-loader", "markdown-loader" ]
49 | }
50 | ]
51 | }
52 | };
--------------------------------------------------------------------------------
/examples/asyncValidation/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'babel-polyfill',
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/dist/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | NODE_ENV: JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | resolve: {
29 | modulesDirectories: [
30 | 'src',
31 | 'node_modules'
32 | ],
33 | extensions: [ '', '.json', '.js' ]
34 | },
35 | module: {
36 | loaders: [
37 | {
38 | test: /\.js$/,
39 | loaders: [ 'babel' ],
40 | include: path.join(__dirname, 'src')
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json-loader'
45 | },
46 | {
47 | test: /\.md/,
48 | loaders: [ "html-loader", "markdown-loader" ]
49 | }
50 | ]
51 | }
52 | };
--------------------------------------------------------------------------------
/examples/remoteSubmit/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'babel-polyfill',
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/dist/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | NODE_ENV: JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | resolve: {
29 | modulesDirectories: [
30 | 'src',
31 | 'node_modules'
32 | ],
33 | extensions: [ '', '.json', '.js' ]
34 | },
35 | module: {
36 | loaders: [
37 | {
38 | test: /\.js$/,
39 | loaders: [ 'babel' ],
40 | include: path.join(__dirname, 'src')
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json-loader'
45 | },
46 | {
47 | test: /\.md/,
48 | loaders: [ "html-loader", "markdown-loader" ]
49 | }
50 | ]
51 | }
52 | };
--------------------------------------------------------------------------------
/examples/submitValidation/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'babel-polyfill',
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/dist/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | NODE_ENV: JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | resolve: {
29 | modulesDirectories: [
30 | 'src',
31 | 'node_modules'
32 | ],
33 | extensions: [ '', '.json', '.js' ]
34 | },
35 | module: {
36 | loaders: [
37 | {
38 | test: /\.js$/,
39 | loaders: [ 'babel' ],
40 | include: path.join(__dirname, 'src')
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json-loader'
45 | },
46 | {
47 | test: /\.md/,
48 | loaders: [ "html-loader", "markdown-loader" ]
49 | }
50 | ]
51 | }
52 | };
--------------------------------------------------------------------------------
/examples/syncValidation/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'babel-polyfill',
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/dist/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | NODE_ENV: JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | resolve: {
29 | modulesDirectories: [
30 | 'src',
31 | 'node_modules'
32 | ],
33 | extensions: [ '', '.json', '.js' ]
34 | },
35 | module: {
36 | loaders: [
37 | {
38 | test: /\.js$/,
39 | loaders: [ 'babel' ],
40 | include: path.join(__dirname, 'src')
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json-loader'
45 | },
46 | {
47 | test: /\.md/,
48 | loaders: [ "html-loader", "markdown-loader" ]
49 | }
50 | ]
51 | }
52 | };
--------------------------------------------------------------------------------
/examples/fieldLevelValidation/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'babel-polyfill',
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/dist/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | NODE_ENV: JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | resolve: {
29 | modulesDirectories: [
30 | 'src',
31 | 'node_modules'
32 | ],
33 | extensions: [ '', '.json', '.js' ]
34 | },
35 | module: {
36 | loaders: [
37 | {
38 | test: /\.js$/,
39 | loaders: [ 'babel' ],
40 | include: path.join(__dirname, 'src')
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json-loader'
45 | },
46 | {
47 | test: /\.md/,
48 | loaders: [ "html-loader", "markdown-loader" ]
49 | }
50 | ]
51 | }
52 | };
--------------------------------------------------------------------------------
/examples/initializeFromState/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'babel-polyfill',
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/dist/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | NODE_ENV: JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | resolve: {
29 | modulesDirectories: [
30 | 'src',
31 | 'node_modules'
32 | ],
33 | extensions: [ '', '.json', '.js' ]
34 | },
35 | module: {
36 | loaders: [
37 | {
38 | test: /\.js$/,
39 | loaders: [ 'babel' ],
40 | include: path.join(__dirname, 'src')
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json-loader'
45 | },
46 | {
47 | test: /\.md/,
48 | loaders: [ "html-loader", "markdown-loader" ]
49 | }
50 | ]
51 | }
52 | };
--------------------------------------------------------------------------------
/examples/selectingFormValues/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'babel-polyfill',
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/dist/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | NODE_ENV: JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | resolve: {
29 | modulesDirectories: [
30 | 'src',
31 | 'node_modules'
32 | ],
33 | extensions: [ '', '.json', '.js' ]
34 | },
35 | module: {
36 | loaders: [
37 | {
38 | test: /\.js$/,
39 | loaders: [ 'babel' ],
40 | include: path.join(__dirname, 'src')
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json-loader'
45 | },
46 | {
47 | test: /\.md/,
48 | loaders: [ "html-loader", "markdown-loader" ]
49 | }
50 | ]
51 | }
52 | };
--------------------------------------------------------------------------------
/examples/wizard/src/WizardForm.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 | import WizardFormFirstPage from './WizardFormFirstPage'
3 | import WizardFormSecondPage from './WizardFormSecondPage'
4 | import WizardFormThirdPage from './WizardFormThirdPage'
5 |
6 | class WizardForm extends Component {
7 | constructor(props) {
8 | super(props)
9 | this.nextPage = this.nextPage.bind(this)
10 | this.previousPage = this.previousPage.bind(this)
11 | this.state = {
12 | page: 1
13 | }
14 | }
15 | nextPage() {
16 | this.setState({ page: this.state.page + 1 })
17 | }
18 |
19 | previousPage() {
20 | this.setState({ page: this.state.page - 1 })
21 | }
22 |
23 | render() {
24 | const { onSubmit } = this.props
25 | const { page } = this.state
26 | return (
27 | {page === 1 && }
28 | {page === 2 && }
29 | {page === 3 && }
30 |
31 | )
32 | }
33 | }
34 |
35 | WizardForm.propTypes = {
36 | onSubmit: PropTypes.func.isRequired
37 | }
38 |
39 | export default WizardForm
40 |
--------------------------------------------------------------------------------
/examples/immutable/src/ImmutableForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Field, reduxForm } from 'redux-form/immutable' // <--- immutable import
3 | import validate from './validate'
4 |
5 | const renderField = ({ input, label, type, meta: { touched, error } }) => (
6 |
7 |
8 |
9 |
10 | {touched && error && {error}}
11 |
12 |
13 | )
14 |
15 | const ImmutableForm = (props) => {
16 | const { handleSubmit, pristine, reset, submitting } = props
17 | return (
18 |
27 | )
28 | }
29 |
30 | export default reduxForm({
31 | form: 'immutableExample', // a unique identifier for this form
32 | validate
33 | })(ImmutableForm)
34 |
--------------------------------------------------------------------------------
/src/selectors/isValid.js:
--------------------------------------------------------------------------------
1 | import createHasError from '../hasError'
2 |
3 | const createIsValid = structure => {
4 | const { getIn } = structure
5 | const hasError = createHasError(structure)
6 | return (form, getFormState = state => getIn(state, 'form'), ignoreSubmitErrors = false) =>
7 | state => {
8 | const formState = getFormState(state)
9 | const syncError = getIn(formState, `${form}.syncError`)
10 | if (syncError) {
11 | return false
12 | }
13 | if (!ignoreSubmitErrors) {
14 | const error = getIn(formState, `${form}.error`)
15 | if (error) {
16 | return false
17 | }
18 | }
19 | const syncErrors = getIn(formState, `${form}.syncErrors`)
20 | const asyncErrors = getIn(formState, `${form}.asyncErrors`)
21 | const submitErrors = ignoreSubmitErrors ?
22 | undefined :
23 | getIn(formState, `${form}.submitErrors`)
24 | if (!syncErrors && !asyncErrors && !submitErrors) {
25 | return true
26 | }
27 |
28 | const registeredFields = getIn(formState, `${form}.registeredFields`) || []
29 | return !registeredFields.some(field => hasError(field, syncErrors, asyncErrors, submitErrors))
30 | }
31 | }
32 |
33 | export default createIsValid
34 |
--------------------------------------------------------------------------------
/src/__tests__/reducer.unregisterField.spec.js:
--------------------------------------------------------------------------------
1 | import { unregisterField } from '../actions'
2 |
3 | const describeUnregisterField = (reducer, expect, { fromJS }) => () => {
4 | it('should remove a field from registeredFields', () => {
5 | const state = reducer(fromJS({
6 | foo: {
7 | registeredFields: [ { name: 'bar', type: 'field' } ]
8 | }
9 | }), unregisterField('foo', 'bar'))
10 | expect(state)
11 | .toEqualMap({
12 | foo: {}
13 | })
14 | })
15 |
16 | it('should do nothing if there are no registered fields', () => {
17 | const initialState = fromJS({
18 | foo: {}
19 | })
20 | const state = reducer(initialState, unregisterField('foo', 'bar'))
21 | expect(state)
22 | .toEqual(initialState)
23 | })
24 |
25 | it('should do nothing if the field is not registered', () => {
26 | const state = reducer(fromJS({
27 | foo: {
28 | registeredFields: [
29 | { name: 'bar', type: 'field' }
30 | ]
31 | }
32 | }), unregisterField('foo', 'baz'))
33 | expect(state)
34 | .toEqualMap({
35 | foo: {
36 | registeredFields: [ { name: 'bar', type: 'field' } ]
37 | }
38 | })
39 | })
40 | }
41 |
42 | export default describeUnregisterField
43 |
--------------------------------------------------------------------------------
/docs/faq/CustomComponent.md:
--------------------------------------------------------------------------------
1 | # Will `redux-form` work with my custom input component?
2 |
3 | The minimum interface needed for a custom component to work with `redux-form` is to make sure that
4 | `value` and `onChange` are passed properly. These are pretty standard prop names, so it's
5 | possible that your component will work right out of the box.
6 |
7 | But let's say that you have a custom component called `MyStrangeInput` that has `currentValue`
8 | and `thingsChanged` props that expect the value to be wrapped in an object under a `val` key. You
9 | would have to do something like:
10 |
11 | ```javascript
12 | const renderMyStrangeInput = field => (
13 | field.input.onChange(param.val)}/>
16 | )
17 |
18 | ...
19 |
20 | render() {
21 | return (
22 |
23 |
24 |
25 | );
26 | }
27 | ```
28 |
29 | The point is that almost any input can be adjusted to use `redux-form`. If you are using an input
30 | component with a non-standard interface many times in your application, it is recommended that
31 | you wrap it in another component that will allow you to do the normal field destructuring:
32 | ``.
33 |
--------------------------------------------------------------------------------
/src/__tests__/reducer.arrayShift.spec.js:
--------------------------------------------------------------------------------
1 | import { arrayShift } from '../actions'
2 |
3 | const describeArrayShift = (reducer, expect, { fromJS }) => () => {
4 | it('should remove from beginning', () => {
5 | const state = reducer(fromJS({
6 | foo: {
7 | values: {
8 | myField: {
9 | subField: [ 'a', 'b', 'c', 'd' ]
10 | }
11 | },
12 | fields: {
13 | myField: {
14 | subField: [
15 | { touched: true, visited: true },
16 | { touched: true },
17 | { touched: true, visited: true },
18 | { touched: true }
19 | ]
20 | }
21 | }
22 | }
23 | }), arrayShift('foo', 'myField.subField'))
24 | expect(state)
25 | .toEqualMap({
26 | foo: {
27 | values: {
28 | myField: {
29 | subField: [ 'b', 'c', 'd' ]
30 | }
31 | },
32 | fields: {
33 | myField: {
34 | subField: [
35 | { touched: true },
36 | { touched: true, visited: true },
37 | { touched: true }
38 | ]
39 | }
40 | }
41 | }
42 | })
43 | })
44 | }
45 |
46 | export default describeArrayShift
47 |
--------------------------------------------------------------------------------
/docs/faq/EnterToSubmit.md:
--------------------------------------------------------------------------------
1 | # How can I submit my form when the user presses Enter?
2 |
3 | The default browser behavior for `text` and `password` inputs when ↵ is pressed is to activate the first `