├── .babelrc.js
├── .codesandbox
└── ci.json
├── .eslintrc
├── .flowconfig
├── .github
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── FUNDING.yml
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── ci.yml
│ └── lock.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .travis.yml
├── LICENSE
├── README.md
├── banner.png
├── docs
├── HolyJS2018.gif
├── ReactAlicante2018.gif
├── api.md
├── api
│ ├── Field.md
│ ├── Form.md
│ ├── FormSpy.md
│ ├── useField.md
│ ├── useForm.md
│ └── useFormState.md
├── examples.md
├── examples
│ ├── chakra.md
│ ├── field-level-validation.md
│ ├── record-level-validation.md
│ ├── simple.md
│ ├── submission-errors.md
│ ├── subscriptions.md
│ └── wizard.md
├── faq.md
├── getting-started.md
├── logo.pdf
├── logo.svg
├── migration
│ ├── formik.md
│ └── redux-form.md
├── philosophy.md
├── sencha.svg
└── types
│ ├── FieldProps.md
│ ├── FieldRenderProps.md
│ ├── FormProps.md
│ ├── FormRenderProps.md
│ ├── FormSpyProps.md
│ └── FormSpyRenderProps.md
├── eslint.config.js
├── examples
├── async-field-level-validation
│ ├── Spinner.js
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── async-redux-submission
│ ├── Styles.js
│ ├── asyncSubmissionMiddleware.js
│ ├── index.js
│ ├── package.json
│ ├── readme.md
│ ├── registrationDuck.js
│ └── store.js
├── async-typeahead-redux
│ ├── GithubUserTypeahead.jsx
│ ├── Styles.js
│ ├── actions.js
│ ├── index.js
│ ├── package.json
│ ├── readme.md
│ ├── store.js
│ └── useKeyword.js
├── auto-save-field-blur
│ ├── AutoSave.js
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── auto-save-selective-debounce
│ ├── AutoSave.js
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── auto-save-with-debounce
│ ├── AutoSave.js
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── calculated-fields
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── chakra
│ ├── index.js
│ ├── package.json
│ ├── readme.md
│ └── validate.js
├── conditional-fields
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ ├── pickupTimes.js
│ └── readme.md
├── credit-card
│ ├── Card.js
│ ├── Styles.js
│ ├── cardUtils.js
│ ├── index.js
│ ├── package.json
│ ├── readme.md
│ └── sandbox.config.json
├── custom-validation-engine
│ ├── OnBlurValidation.js
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── debounced-record-level-validation
│ ├── ErrorWithDelay.js
│ ├── README.md
│ ├── Styles.js
│ ├── index.js
│ └── package.json
├── declarative-form-rules
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── downshift-typeahead
│ ├── DownshiftInput.js
│ ├── Styles.js
│ ├── fruit.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── external-submit
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── field-arrays
│ ├── Styles.js
│ ├── index.js
│ ├── package-lock.json
│ ├── package.json
│ └── readme.md
├── field-level-validation
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── field-warnings
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ ├── readme.md
│ └── warning-engine.js
├── fields-component
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── focus-first-error
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ ├── readme.md
│ └── validate.js
├── format-on-blur
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── format-string-by-pattern
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── hybrid-sync-async-record-level-validation
│ ├── Spinner.js
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── independent-error-component-render-props
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── independent-error-component-with-hooks
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── listening-for-external-changes
│ ├── BooleanDecay.js
│ ├── ExternalModificationDetector.js
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── loading-initializing-values
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── loading-saving-reinitializing
│ ├── LoadSaveReinitializeForm.js
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── material-ui
│ ├── .prettierrc
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── parse-format
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── prefixed-fields
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── readme.md
├── record-level-validation
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── redux
│ ├── FormStateFromRedux.js
│ ├── FormStateToRedux.js
│ ├── Styles.js
│ ├── finalFormDuck.js
│ ├── index.js
│ ├── package.json
│ ├── readme.md
│ └── store.js
├── reusable-field-groups
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── simple
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── strongly-typed-values-typescript
│ ├── Styles.tsx
│ ├── components
│ │ ├── CheckboxInput.tsx
│ │ ├── MultiCheckboxInput.tsx
│ │ ├── MultiSelectInput.tsx
│ │ ├── NumberInput.tsx
│ │ ├── RadioInput.tsx
│ │ ├── SelectInput.tsx
│ │ ├── TextAreaInput.tsx
│ │ └── TextInput.tsx
│ ├── index.tsx
│ └── readme.md
├── styling-with-smooth-ui
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── submission-errors
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── subscriptions
│ ├── RenderCount.js
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── third-party-components
│ ├── Styles.js
│ ├── index.js
│ ├── package.json
│ ├── readme.md
│ └── states.js
└── wizard
│ ├── Styles.js
│ ├── Wizard.js
│ ├── index.js
│ ├── package.json
│ └── readme.md
├── form-nerd-logo.png
├── logo.png
├── package-scripts.js
├── package.json
├── rollup.config.js
├── src
├── Field.test.js
├── Field.tsx
├── FormSpy.test.js
├── FormSpy.tsx
├── ReactFinalForm.test.js
├── ReactFinalForm.tsx
├── context.ts
├── getValue.test.js
├── getValue.ts
├── getters.ts
├── index.ts
├── isReactNative.ts
├── isSyntheticEvent.ts
├── renderComponent.test.js
├── renderComponent.ts
├── shallowEqual.test.js
├── shallowEqual.ts
├── testUtils.ts
├── types.ts
├── useConstant.ts
├── useConstantCallback.test.js
├── useConstantCallback.ts
├── useField.test.js
├── useField.ts
├── useForm.test.js
├── useForm.ts
├── useFormState.test.js
├── useFormState.ts
├── useLatest.ts
└── useWhenValueChanges.ts
├── tsconfig.build.json
├── tsconfig.json
├── tslint.json
├── typescript
├── Field.test.tsx
├── FormSpy.test.tsx
├── ReactFinalForm.test.tsx
├── index.d.ts
├── tsconfig.json
├── useField.test.tsx
└── useFormState.test.tsx
└── yarn.lock
/.babelrc.js:
--------------------------------------------------------------------------------
1 | const { NODE_ENV } = process.env;
2 | const test = NODE_ENV === "test";
3 | const loose = true;
4 |
5 | module.exports = {
6 | presets: [
7 | [
8 | "@babel/preset-env",
9 | {
10 | loose,
11 | ...(test ? { targets: { node: "8" } } : {}),
12 | },
13 | ],
14 | "@babel/preset-react",
15 | "@babel/preset-typescript",
16 | ],
17 | plugins: [
18 | "@babel/plugin-syntax-dynamic-import",
19 | "@babel/plugin-syntax-import-meta",
20 | test && "@babel/plugin-transform-react-jsx-source",
21 | ].filter(Boolean),
22 | };
23 |
--------------------------------------------------------------------------------
/.codesandbox/ci.json:
--------------------------------------------------------------------------------
1 | {
2 | "sandboxes": [
3 | "/examples/simple",
4 | "/examples/record-level-validation",
5 | "/examples/field-level-validation",
6 | "/examples/submission-errors",
7 | "/examples/subscriptions"
8 | ],
9 | "node": "18",
10 | "installCommand": "setup"
11 | }
12 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "plugins": ["react-hooks"],
4 | "rules": {
5 | "jsx-a11y/href-no-hash": 0,
6 | "react-hooks/rules-of-hooks": "error",
7 | "react-hooks/exhaustive-deps": "warn",
8 | "import/no-anonymous-default-export": 0
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | dist
3 |
4 | [include]
5 |
6 | [libs]
7 |
8 | [options]
9 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thanks for your interest in contributing to 🏁 React Final Form! Please take a
4 | moment to review this document **before submitting a pull request**.
5 |
6 | We are open to, and grateful for, any contributions made by the community.
7 |
8 | ## Reporting issues and asking questions
9 |
10 | Before opening an issue, please search
11 | the [issue tracker](https://github.com/final-form/react-final-form/issues) to
12 | make sure your issue hasn’t already been reported.
13 |
14 | **We use the issue tracker to keep track of bugs and improvements** to 🏁 React
15 | Final Form itself, its examples, and the documentation. We encourage you to open
16 | issues to discuss improvements, architecture, internal implementation, etc. If a
17 | topic has been discussed before, we will ask you to join the previous
18 | discussion.
19 |
20 | For support or usage questions, please search and ask on
21 | [StackOverflow with a `react-final-form` tag](https://stackoverflow.com/questions/tagged/react-final-form).
22 | We ask you to do this because StackOverflow has a much better job at keeping
23 | popular questions visible. Unfortunately good answers get lost and outdated on
24 | GitHub.
25 |
26 | **If you already asked at StackOverflow and still got no answers, post an issue
27 | with the question link, so we can either answer it or evolve into a bug/feature
28 | request.**
29 |
30 | ## Sending a pull request
31 |
32 | **Please ask first before starting work on any significant new features.**
33 |
34 | It's never a fun experience to have your pull request declined after investing a
35 | lot of time and effort into a new feature. To avoid this from happening, we
36 | request that contributors create
37 | [an issue](https://github.com/final-form/react-final-form/issues) to first
38 | discuss any significant new features.
39 |
40 | Please try to keep your pull request focused in scope and avoid including
41 | unrelated commits.
42 |
43 | After you have submitted your pull request, we’ll try to get back to you as soon
44 | as possible. We may suggest some changes or improvements.
45 |
46 | Please format the code before submitting your pull request by running:
47 |
48 | ```sh
49 | npm run precommit
50 | ```
51 |
52 | ## Coding standards
53 |
54 | Our code formatting rules are defined in
55 | [.eslintrc](https://github.com/final-form/react-final-form/blob/master/.eslintrc).
56 | You can check your code against these standards by running:
57 |
58 | ```sh
59 | npm start lint
60 | ```
61 |
62 | To automatically fix any style violations in your code, you can run:
63 |
64 | ```sh
65 | npm run precommit
66 | ```
67 |
68 | ## Running tests
69 |
70 | You can run the test suite using the following commands:
71 |
72 | ```sh
73 | npm test
74 | ```
75 |
76 | Please ensure that the tests are passing when submitting a pull request. If
77 | you're adding new features to 🏁 React Final Form, please include tests.
78 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: erikras
4 | patreon: erikras
5 | open_collective: final-form
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with a single custom sponsorship URL
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
6 |
7 | ### Are you submitting a **bug report** or a **feature request**?
8 |
9 |
10 |
11 | ### What is the current behavior?
12 |
13 |
14 |
15 | ### What is the expected behavior?
16 |
17 | ### Sandbox Link
18 |
19 |
20 |
21 | ### What's your environment?
22 |
23 |
24 |
25 | ### Other information
26 |
27 |
28 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
27 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | lint:
7 | name: Lint
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v2
12 | - name: Use Node.js ${{ matrix.node_version }}
13 | uses: actions/setup-node@v2
14 | with:
15 | node-version: "22"
16 | - name: Prepare env
17 | run: yarn install --ignore-scripts --frozen-lockfile
18 | - name: Run linter
19 | run: yarn start lint
20 |
21 | prettier:
22 | name: Prettier Check
23 | runs-on: ubuntu-latest
24 |
25 | steps:
26 | - uses: actions/checkout@v2
27 | - name: Use Node.js ${{ matrix.node_version }}
28 | uses: actions/setup-node@v2
29 | with:
30 | node-version: "22"
31 | - name: Prepare env
32 | run: yarn install --ignore-scripts --frozen-lockfile
33 | - name: Run prettier
34 | run: yarn start prettier
35 |
36 | test:
37 | name: Unit Tests
38 | runs-on: ubuntu-latest
39 |
40 | steps:
41 | - uses: actions/checkout@v2
42 | - name: Use Node.js ${{ matrix.node_version }}
43 | uses: actions/setup-node@v2
44 | with:
45 | node-version: "22"
46 | - name: Prepare env
47 | run: yarn install --ignore-scripts --frozen-lockfile
48 | - name: Run unit tests
49 | run: yarn start test
50 | - name: Run code coverage
51 | uses: codecov/codecov-action@v2.1.0
52 |
--------------------------------------------------------------------------------
/.github/workflows/lock.yml:
--------------------------------------------------------------------------------
1 | name: "Lock Threads"
2 |
3 | on:
4 | schedule:
5 | - cron: "0 * * * *"
6 | workflow_dispatch:
7 |
8 | permissions:
9 | issues: write
10 | pull-requests: write
11 |
12 | concurrency:
13 | group: lock
14 |
15 | jobs:
16 | action:
17 | runs-on: ubuntu-latest
18 | steps:
19 | - uses: dessant/lock-threads@v3
20 | with:
21 | issue-inactive-days: "365"
22 | issue-lock-reason: "resolved"
23 | pr-inactive-days: "365"
24 | pr-lock-reason: "resolved"
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | *.iml
3 | .nyc_output
4 | coverage
5 | flow-coverage
6 | node_modules
7 | dist
8 | lib
9 | es
10 | npm-debug.log
11 | .DS_Store
12 | .yalc
13 | yalc.lock
14 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | coverage
2 | dist
3 | node_modules
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all"
3 | }
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | before_install:
4 | - npm install -g npm@6.4.0
5 | cache:
6 | directories:
7 | - node_modules
8 | notifications:
9 | email: false
10 | node_js:
11 | - "10"
12 | - "12"
13 | - "14"
14 | script:
15 | - npm start validate
16 | after_success:
17 | - npx codecov
18 | branches:
19 | only:
20 | - main
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🏁 React Final Form
2 |
3 | [](https://final-form.org/react)
4 |
5 | [](#backers) [](#sponsors) [](https://www.npmjs.com/package/react-final-form)
6 | [](https://www.npmjs.com/package/react-final-form)
7 | [](https://travis-ci.org/final-form/react-final-form)
8 | [](https://codecov.io/gh/final-form/react-final-form)
9 | [](https://github.com/prettier/prettier)
10 |
11 | ✅ Zero dependencies (that affect your bundle size)
12 |
13 | ✅ Only peer dependencies: React and
14 | [🏁 Final Form](https://github.com/final-form/final-form#-final-form)
15 |
16 | ✅ Opt-in subscriptions - only update on the state you need!
17 |
18 | ✅ 💥 [**3.0k gzipped**](https://bundlephobia.com/result?p=react-final-form) 💥
19 |
20 | ---
21 |
22 | [](https://www.sencha.com/)
23 |
24 | ### React Final Form is sponsored by [Sencha](https://www.sencha.com/).
25 |
26 | Comprehensive JS framework and UI components for building enterprise-grade web apps.
27 |
28 | ---
29 |
30 | ## 💬 [Give Feedback on React Final Form](https://goo.gl/forms/dxdfxKNy64DLb99z2) 💬
31 |
32 | In the interest of making 🏁 React Final Form the best library it can be, we'd love your thoughts and feedback.
33 |
34 | [Take a quick survey](https://goo.gl/forms/dxdfxKNy64DLb99z2).
35 |
36 | ---
37 |
38 | React Final Form is a thin React wrapper for [Final Form](https://final-form.org), which is a subscriptions-based form state management library that uses the [Observer pattern](https://en.wikipedia.org/wiki/Observer_pattern), so only the components that need updating are re-rendered as the form's state changes.
39 |
40 | ## [Getting Started](https://final-form.org/docs/react-final-form/getting-started)
41 |
42 | ## [Philosophy](https://final-form.org/docs/react-final-form/philosophy)
43 |
44 | ## [Examples](https://final-form.org/docs/react-final-form/examples)
45 |
46 | ## [API](https://final-form.org/docs/react-final-form/api)
47 |
48 | ## [FAQ](https://final-form.org/docs/react-final-form/faq)
49 |
50 |
51 |
--------------------------------------------------------------------------------
/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/final-form/react-final-form/5a64284e1e10f67dfdf1bbaa549631bf95a742f0/banner.png
--------------------------------------------------------------------------------
/docs/HolyJS2018.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/final-form/react-final-form/5a64284e1e10f67dfdf1bbaa549631bf95a742f0/docs/HolyJS2018.gif
--------------------------------------------------------------------------------
/docs/ReactAlicante2018.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/final-form/react-final-form/5a64284e1e10f67dfdf1bbaa549631bf95a742f0/docs/ReactAlicante2018.gif
--------------------------------------------------------------------------------
/docs/api.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api). Links may not work on Github.com.
2 |
3 | # API
4 |
5 | The API for React Final Form consists of three components and three hooks:
6 |
7 | ## Components
8 |
9 | ### [`
`](api/Form)
10 |
11 | A component that surrounds your entire form and manages the form state. It can inject form state and functionality, e.g. a `handleSubmit` function for you to pass to your `` and creates a "field". It register itself with the surrounding `` tag and manages all the state for a particular field, providing input callbacks (e.g. `onBlur`, `onChange`, and `onFocus`) as well as the value of the form and myriad metadata about the state of the field.
16 |
17 | ### [``](api/FormSpy)
18 |
19 | _[Advanced Usage]_ A component that can tap into form-wide state from inside your ``. It's primarily only for advanced usage when form renders are being restricted via the `subscription` prop.
20 |
21 | ## Hooks
22 |
23 | ### [`useField()`](api/useField)
24 |
25 | A hook that will convert any of your components into a `` component, registering with the surrounding `` and providing field state to your component. `useField()` is used internally by ``.
26 |
27 | ### [`useForm()`](api/useForm)
28 |
29 | A hook that will pluck the Final Form [`form` instance](/docs/final-form/types/FormApi) out of context.
30 |
31 | ### [`useFormState()`](api/useFormState)
32 |
33 | A hook that will convert any of your components into a `` component, allowing fine-grained control over subscribing to parts of the form state.
34 |
--------------------------------------------------------------------------------
/docs/api/FormSpy.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/FormSpy). Links may not work on Github.com.
2 |
3 | # ``
4 |
5 | ```ts
6 | import { FormSpy } from 'react-final-form'
7 | ```
8 |
9 | A component that subscribes to form state, and injects both form state and the `form` instance via a render prop.
10 |
11 | The `` will rerender any time the form state it is subscribed to changes. By default it subscribes to _all_ form state. You can control which form state it subscribes to with the `subscription` prop.
12 |
13 | By providing an `onChange` prop, `` can also be used to execute code when a particular part of form state changes.
14 |
15 | ## Props
16 |
17 | `` accepts [`FormSpyProps`](../types/FormSpyProps) and will call the render function with [`FormSpyRenderProps`](../types/FormSpyRenderProps).
18 |
19 | The only required prop is one of `onChange`, `component`, `render`, or `children`.
20 |
21 | ## Basic Usage
22 |
23 | It should be noted that `` is for very advanced use cases.
24 |
25 | **If you are not restricting your form state by providing a `subscription` prop to ``, you probably do not need ``!** Just use the form state injected by ``.
26 |
27 | You need to do _one_ of two things when using ``:
28 |
29 | ### 1. Provide a way to render the form state
30 |
31 | There are three ways to render a `` component:
32 |
33 | | Prop | Type |
34 | | ---------------------- | --------------------- |
35 | | `` | `React.ComponentType` |
36 | | `` | `Function` |
37 | | `` | `Function` |
38 |
39 | The only important distinction is that if you pass a `component` prop, it will be rendered with [`React.createElement()`](https://reactjs.org/docs/react-api.html#createelement), resulting in your component actually being in the React node tree, i.e. inspectable in [DevTools](https://github.com/facebook/react-devtools#react-developer-tools-).
40 |
41 | ```tsx
42 | // Render a reset button that will be
43 | // disabled when the form is pristine
44 |
45 | {props => (
46 |
53 | )}
54 |
55 | ```
56 |
57 | ### 2. Pass an `onChange` callback
58 |
59 | `` can sometimes be useful to execute code when a particular part of form state changes. This is what the `onChange` callback is for.
60 |
61 | **If you pass `onChange`, nothing will be rendered.**
62 |
63 | ```tsx
64 | {
67 | console.log('Form validity changed to', props.valid)
68 | }}
69 | />
70 | ```
71 |
--------------------------------------------------------------------------------
/docs/api/useField.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/useField). Links may not work on Github.com.
2 |
3 | # `useField()`
4 |
5 | ```ts
6 | import { useField } from 'react-final-form'
7 | ```
8 |
9 |
10 | ```ts
11 | (name: string, config: UseFieldConfig) => FieldRenderProps
12 | ```
13 |
14 | The `useField()` hook takes two parameters:
15 |
16 | ### `name`
17 |
18 | ```ts
19 | string
20 | ```
21 |
22 | **Required**
23 |
24 | The name of the field.
25 |
26 | ### `config`
27 |
28 | ```ts
29 | UseFieldConfig
30 | ```
31 |
32 | Optional.
33 |
34 | An object that looks just like [`FieldProps`](../types/FieldProps), except without the name.
35 |
36 | `useField()` returns [`FieldRenderProps`](../types/FieldRenderProps). It will manage the rerendering of any component you use it in, i.e. the component will only rerender if the field state subscribed to via `useField()` changes.
37 |
38 | `useField()` is used internally inside [``](Field).
39 |
--------------------------------------------------------------------------------
/docs/api/useForm.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/useForm). Links may not work on Github.com.
2 |
3 | # `useForm()`
4 |
5 | ```ts
6 | import { useForm } from 'react-final-form'
7 | ```
8 |
9 |
10 | ```ts
11 | () => FormApi
12 | ```
13 |
14 | The `useForm()` hook plucks the [`FormApi`](/docs/final-form/types/FormApi) out of the React context for you. It will throw an exception if you try to use it outside of a [``](Form) component.
15 |
16 | `useForm()` is used internally inside [`useField()`](useField), [``](Field), and [``](FormSpy).
17 |
--------------------------------------------------------------------------------
/docs/api/useFormState.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/api/useFormState). Links may not work on Github.com.
2 |
3 | # `useFormState`
4 |
5 | ```ts
6 | import { useFormState } from 'react-final-form'
7 | ```
8 |
9 | The `useFormState()` hook takes one optional parameter, which matches the exact shape of [`FormSpyProps`](../types/FormSpyProps) (except without the render props). It returns a [`FormState`](/docs/final-form/types/FormState).
10 |
11 | `useFormState()` is used internally inside [``](FormSpy).
12 |
--------------------------------------------------------------------------------
/docs/examples/chakra.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/chakra). Links may not work on Github.com.
2 |
3 | # Chakra UI Example
4 |
5 | Demonstrates how to use [Chakra UI](https://chakra-ui.com) components with React Final Form.
6 |
7 | [](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/chakra)
8 |
--------------------------------------------------------------------------------
/docs/examples/field-level-validation.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/field-level-validation). Links may not work on Github.com.
2 |
3 | # Field Level Validation Example
4 |
5 | Introduces field-level validation functions and demonstrates how to display errors next to fields using child render functions.
6 |
7 | [](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/field-level-validation)
8 |
--------------------------------------------------------------------------------
/docs/examples/record-level-validation.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/record-level-validation). Links may not work on Github.com.
2 |
3 | # Record-Level Example
4 |
5 | Introduces a whole-record validation function and demonstrates how to display errors next to fields using child render functions.
6 |
7 | [](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/record-level-validation)
8 |
--------------------------------------------------------------------------------
/docs/examples/simple.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/simple). Links may not work on Github.com.
2 |
3 | # Simple Example
4 |
5 | Uses the built-in React inputs: input, select, and textarea to build a form with no validation.
6 |
7 | [](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/simple)
8 |
--------------------------------------------------------------------------------
/docs/examples/submission-errors.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/submission-errors). Links may not work on Github.com.
2 |
3 | # Submission Errors
4 |
5 | Demonstrates how to return submission errors from failed submits. Notice that the `Promise` should _resolve_ to the submission error (not reject). Rejection is reserved for communications or server exceptions.
6 |
7 | [](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/submission-errors)
8 |
--------------------------------------------------------------------------------
/docs/examples/subscriptions.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/subscriptions). Links may not work on Github.com.
2 |
3 | # High Performance Through Subscriptions Example
4 |
5 | Demonstrates how, by restricting which parts of form state the form component needs to render, it reduces the number of times the whole form has to rerender. Yet, if some part of form state is needed inside of it, the [``](../api/FormSpy) component can be used to attain it.
6 |
7 | [](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/subscriptions)
8 |
--------------------------------------------------------------------------------
/docs/examples/wizard.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/examples/wizard). Links may not work on Github.com.
2 |
3 | # Wizard Form Example
4 |
5 | Demonstrates how to use React Final Form to create a multi-page "wizard" form, with validation on each page.
6 |
7 | [](https://codesandbox.io/s/github/final-form/react-final-form/tree/master/examples/wizard)
8 |
--------------------------------------------------------------------------------
/docs/getting-started.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/getting-started). Links may not work on Github.com.
2 |
3 | # Getting Started
4 |
5 | Before we jump right into code, you might want to learn a little bit about the [philosophy](philosophy) and origin story of React Final Form.
6 |
7 | ## Installation
8 |
9 | ```bash
10 | npm install --save final-form react-final-form
11 | ```
12 |
13 | or
14 |
15 | ```bash
16 | yarn add final-form react-final-form
17 | ```
18 |
19 | ## Architecture
20 |
21 | React Final Form is a thin React wrapper for [Final Form](/), which is a subscriptions-based form state management library that uses the [Observer pattern](https://en.wikipedia.org/wiki/Observer_pattern), so only the components that need updating are re-rendered as the form's state changes.
22 |
23 | By default, **React Final Form subscribes to _all_ changes**, but if you want to fine tune your form to optimized blazing-fast perfection, you may specify only the form state that you care about for rendering your gorgeous UI. You can think of it a little like GraphQL's feature of only fetching the data your component needs to render, and nothing else.
24 |
25 | ## Code
26 |
27 | Here's what it looks like in your code:
28 |
29 | ```jsx
30 | import { Form, Field } from 'react-final-form'
31 |
32 | const MyForm = () => (
33 |
75 | )}
76 | />
77 | )
78 | ```
79 |
80 | [Let's explore the API...](api)
81 |
--------------------------------------------------------------------------------
/docs/logo.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/final-form/react-final-form/5a64284e1e10f67dfdf1bbaa549631bf95a742f0/docs/logo.pdf
--------------------------------------------------------------------------------
/docs/philosophy.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/philosophy). Links may not work on Github.com.
2 |
3 | # Philosophy
4 |
5 | For several years, I ([@erikras](https://twitter.com/erikras)) actively maintained the first big form library in the React community, [Redux Form](https://redux-form.com). During those years, I learned many lessons, about open source and React, and saw hundreds of forms use cases from around the world. As Redux Form grew in popularity (and bundle size), I received a lot of feedback from the community. React Final Form is my answer to the concerns of the community.
6 |
7 | ## Talk
8 |
9 | In this talk, I explain the journey through Redux Form to the conception and creation of React Final Form.
10 |
11 | [Next Generation Forms with React Final Form – React Alicante 2018, Alicante, Spain](https://youtu.be/WoSzy-4mviQ)
12 |
13 | ## Goals
14 |
15 | React Final Form strives to meet the following goals:
16 |
17 | ### Strongly Typed
18 |
19 | React Final Form provides strong typing via both [Flow](https://flow.org) and [Typescript](https://www.typescriptlang.org) to allow you to catch common bugs _at coding time_.
20 |
21 | ### Modularity
22 |
23 | Just because some forms can be complex doesn't mean that your users should need to download all that code for a simple form! React Final Form and Final Form break out complex functionality into separate packages, so the form state management core doesn't get bloated by complicated use cases. This allows you to _build the form library you need_ for every use case.
24 |
25 | Also, this allows for...
26 |
27 | ### Minimal Bundle Size
28 |
29 | React Final Form is a minimal wrapper around the _zero-dependency_ Final Form core. All React Final Form does is know how to get form values out of [`SyntheticEvent`](https://reactjs.org/docs/events.html) and manage field subscriptions to the form.
30 |
31 | ### High Performance
32 |
33 | You probably won't need to fine-tune your form performance, but if your form grows and starts to lag, you'll be glad you've chosen React Final Form. Every bit of form and field state can be chosen _à la carte_ to trigger a rerender in React.
34 |
35 | If you're familiar with Redux in React, it's a little bit like how you can use [selectors](https://redux.js.org/recipes/computing-derived-data) to specify exactly which "slice" of state you want your component to be notified about.
36 |
37 | The result is that you can streamline your form for maximum performance.
38 |
39 | [Ready to get started?](getting-started)
40 |
--------------------------------------------------------------------------------
/docs/types/FormRenderProps.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FormRenderProps). Links may not work on Github.com.
2 |
3 | # `FormRenderProps`
4 |
5 | These are the props that [``](../api/Form) provides to your render function or component. Keep in mind that the values you receive here are dependent upon which values of [`FormState`](/docs/final-form/types/FormState) you have subscribed to with the [`subscription` prop](FormProps#subscription).
6 |
7 | This object contains everything in Final Form's [`FormState`](/docs/final-form/types/FormState) as well as:
8 |
9 | ## `form`
10 |
11 | ```ts
12 | FormApi
13 | ```
14 |
15 | The Final Form [`FormApi`](/docs/final-form/types/FormApi).
16 |
17 | ## `handleSubmit`
18 |
19 | ```ts
20 | (?SyntheticEvent) => ?Promise
21 | ```
22 |
23 | A function intended for you to give directly to the `
30 | ```
31 |
32 | The function's return type depends on the way the [`onSubmit` function is written](../types/FormProps#onsubmit).
33 |
34 | Related:
35 |
36 | - [`SyntheticEvent`](https://reactjs.org/docs/events.html)
37 |
--------------------------------------------------------------------------------
/docs/types/FormSpyRenderProps.md:
--------------------------------------------------------------------------------
1 | # This documentation is meant to be read on [final-form.org](https://final-form.org/docs/react-final-form/types/FormSpyRenderProps). Links may not work on Github.com.
2 |
3 | # `FormSpyRenderProps`
4 |
5 | These are the props that [``](../api/FormSpy) provides to your render function or component. Keep in mind that the values you receive here are dependent upon which values of [`FormState`](/docs/final-form/types/FormState) you have subscribed to with the [`subscription`](FormSpyProps#subscription) prop.
6 |
7 | This object contains everything in Final Form's [`FormState`](/docs/final-form/types/FormState) as well as:
8 |
9 | ## `form`
10 |
11 | ```ts
12 | FormApi
13 | ```
14 |
15 | The Final Form [`FormApi`](/docs/final-form/types/FormApi).
16 |
--------------------------------------------------------------------------------
/examples/async-field-level-validation/Spinner.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from "styled-components";
2 |
3 | const rotation = keyframes`
4 | from {
5 | -webkit-transform: rotate(0deg);
6 | }
7 | to {
8 | -webkit-transform: rotate(359deg);
9 | }
10 | `;
11 | export default styled.div`
12 | height: 12px;
13 | width: 12px;
14 | margin-left: 5px;
15 | position: absolute;
16 | right: 0;
17 | top: 0;
18 | animation: ${rotation} 0.6s infinite linear;
19 | border-left: 6px solid rgba(0, 174, 239, 0.15);
20 | border-right: 6px solid rgba(0, 174, 239, 0.15);
21 | border-bottom: 6px solid rgba(0, 174, 239, 0.15);
22 | border-top: 6px solid rgba(0, 174, 239, 0.8);
23 | border-radius: 100%;
24 | `;
25 |
--------------------------------------------------------------------------------
/examples/async-field-level-validation/Styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from "styled-components";
2 |
3 | const btn = (light, dark) => css`
4 | white-space: nowrap;
5 | display: inline-block;
6 | border-radius: 5px;
7 | padding: 5px 15px;
8 | font-size: 16px;
9 | color: white;
10 | &:visited {
11 | color: white;
12 | }
13 | background-image: linear-gradient(${light}, ${dark});
14 | border: 1px solid ${dark};
15 | &:hover {
16 | background-image: linear-gradient(${light}, ${dark});
17 | &[disabled] {
18 | background-image: linear-gradient(${light}, ${dark});
19 | }
20 | }
21 | &:visited {
22 | color: black;
23 | }
24 | &[disabled] {
25 | opacity: 0.6;
26 | cursor: not-allowed;
27 | }
28 | `;
29 |
30 | const btnDefault = css`
31 | ${btn("#ffffff", "#d5d5d5")} color: #555;
32 | `;
33 |
34 | const btnPrimary = btn("#4f93ce", "#285f8f");
35 |
36 | export default styled.div`
37 | font-family: sans-serif;
38 |
39 | h1 {
40 | text-align: center;
41 | color: #222;
42 | }
43 |
44 | h2 {
45 | text-align: center;
46 | color: #222;
47 | }
48 |
49 | & > div {
50 | text-align: center;
51 | }
52 |
53 | a {
54 | display: block;
55 | text-align: center;
56 | color: #222;
57 | margin-bottom: 10px;
58 | }
59 |
60 | form {
61 | max-width: 500px;
62 | margin: 10px auto;
63 | border: 1px solid #ccc;
64 | padding: 20px;
65 | box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
66 | border-radius: 3px;
67 | position: relative;
68 |
69 | & > div {
70 | display: flex;
71 | flex-flow: row nowrap;
72 | line-height: 2em;
73 | margin: 5px;
74 | & > label {
75 | color: #333;
76 | width: 110px;
77 | font-size: 1em;
78 | line-height: 32px;
79 | }
80 | & > input,
81 | & > select,
82 | & > textarea {
83 | flex: 1;
84 | padding: 3px 5px;
85 | font-size: 1em;
86 | margin-left: 15px;
87 | border: 1px solid #ccc;
88 | border-radius: 3px;
89 | }
90 | & > input[type="checkbox"] {
91 | margin-top: 7px;
92 | }
93 | & > div {
94 | margin-left: 16px;
95 | & > label {
96 | display: block;
97 | & > input {
98 | margin-right: 3px;
99 | }
100 | }
101 | }
102 | & > span {
103 | line-height: 32px;
104 | margin-left: 10px;
105 | color: #800;
106 | font-weight: bold;
107 | }
108 | }
109 | & > .buttons {
110 | display: flex;
111 | flex-flow: row nowrap;
112 | justify-content: center;
113 | margin-top: 15px;
114 | }
115 | button {
116 | margin: 0 10px;
117 | &[type="submit"] {
118 | ${btnPrimary};
119 | }
120 | &[type="button"] {
121 | ${btnDefault};
122 | }
123 | }
124 | pre {
125 | border: 1px solid #ccc;
126 | background: rgba(0, 0, 0, 0.1);
127 | box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.2);
128 | padding: 20px;
129 | }
130 | }
131 | `;
132 |
--------------------------------------------------------------------------------
/examples/async-field-level-validation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-final-form-asynchronous-field-level-validation-example",
3 | "version": "1.0.0",
4 | "description": "This example demonstrates how field-level validation functions may be asynchronous.",
5 | "keywords": [
6 | "react-final-form",
7 | "final-form",
8 | "react",
9 | "validation",
10 | "asynchronous"
11 | ],
12 | "main": "index.js",
13 | "dependencies": {
14 | "final-form": "4.20.4",
15 | "react": "17.0.2",
16 | "react-dom": "17.0.2",
17 | "react-final-form": "6.5.3",
18 | "styled-components": "4.2.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/async-field-level-validation/readme.md:
--------------------------------------------------------------------------------
1 | # Asynchronous Field-Level Validation
2 |
3 | [](https://codesandbox.io/s/wy7z7q5zx5)
4 |
--------------------------------------------------------------------------------
/examples/async-redux-submission/asyncSubmissionMiddleware.js:
--------------------------------------------------------------------------------
1 | import { REGISTER, REGISTER_SUCCESS } from "./registrationDuck";
2 |
3 | const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
4 |
5 | const submit = async (values) => {
6 | await sleep(200);
7 | if (values.firstName === "John") {
8 | throw Error({ firstName: "No John's Allowed!" });
9 | }
10 | window.alert(JSON.stringify(values, 0, 2));
11 | };
12 |
13 | /** This is to mimic the behavior of one of the various Redux async middlewares */
14 | const asyncSubmissionMiddleware = (store) => (next) => (action) => {
15 | if (action && action.type === REGISTER) {
16 | submit(action.payload).then(
17 | () => store.dispatch({ type: REGISTER_SUCCESS }),
18 | (errors) => {
19 | // NOTE!! We are passing REGISTER_SUCCESS here because 🏁 Final Form expects
20 | // submit errors to come back in a *resolved* promise.
21 | store.dispatch({ type: REGISTER_SUCCESS, payload: errors });
22 | },
23 | );
24 | }
25 | return next(action);
26 | };
27 |
28 | export default asyncSubmissionMiddleware;
29 |
--------------------------------------------------------------------------------
/examples/async-redux-submission/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-final-form-async-redux-submission",
3 | "version": "1.0.0",
4 | "description": "Demonstrates how to use react-redux-promise-listener to construct a promise out of Redux actions to give to 🏁 React Final Form's onSubmit.",
5 | "keywords": [
6 | "form",
7 | "redux",
8 | "async",
9 | "submit",
10 | "promise"
11 | ],
12 | "homepage": "https://codesandbox.io/s/new",
13 | "main": "src/index.js",
14 | "dependencies": {
15 | "final-form": "4.20.4",
16 | "react": "17.0.2",
17 | "react-dom": "17.0.2",
18 | "react-final-form": "6.5.3",
19 | "react-redux": "7.0.3",
20 | "react-redux-promise-listener": "1.0.0",
21 | "react-scripts": "3.0.1",
22 | "redux": "4.0.1",
23 | "redux-promise-listener": "1.1.1",
24 | "styled-components": "4.2.0"
25 | },
26 | "devDependencies": {},
27 | "scripts": {
28 | "start": "react-scripts start",
29 | "build": "react-scripts build",
30 | "test": "react-scripts test --env=jsdom",
31 | "eject": "react-scripts eject"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/examples/async-redux-submission/readme.md:
--------------------------------------------------------------------------------
1 | # Async Redux Submission
2 |
3 | [](https://codesandbox.io/s/x71mx66z8w)
4 |
--------------------------------------------------------------------------------
/examples/async-redux-submission/registrationDuck.js:
--------------------------------------------------------------------------------
1 | // QUACK! This is a duck. https://github.com/erikras/ducks-modular-redux
2 |
3 | // Actions
4 | export const REGISTER = "final-form-examples/registration/REGISTER";
5 | export const REGISTER_SUCCESS =
6 | "final-form-examples/registration/REGISTER_SUCCESS";
7 | export const REGISTER_FAILURE =
8 | "final-form-examples/registration/REGISTER_FAILURE";
9 |
10 | // Reducer
11 | export default function reducer(state = {}, action = {}) {
12 | switch (action.type) {
13 | case REGISTER:
14 | return {
15 | ...state,
16 | registering: true,
17 | };
18 | case REGISTER_SUCCESS:
19 | return {
20 | ...state,
21 | registering: false,
22 | };
23 | case REGISTER_FAILURE:
24 | return {
25 | ...state,
26 | registering: false,
27 | };
28 | default:
29 | return state;
30 | }
31 | }
32 |
33 | // Action Creators
34 |
35 | // Selectors
36 |
--------------------------------------------------------------------------------
/examples/async-redux-submission/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, combineReducers, applyMiddleware, compose } from "redux";
2 | import createReduxPromiseListener from "redux-promise-listener";
3 | import registration from "./registrationDuck";
4 | import asyncSubmissionMiddleware from "./asyncSubmissionMiddleware";
5 |
6 | const reduxPromiseListener = createReduxPromiseListener();
7 |
8 | const logger = (store) => (next) => (action) => {
9 | console.log(action);
10 | return next(action);
11 | };
12 |
13 | const reducer = combineReducers({
14 | registration,
15 | });
16 |
17 | const composeEnhancers =
18 | (typeof window !== "undefined" &&
19 | window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
20 | compose;
21 |
22 | const store = createStore(
23 | reducer,
24 | {},
25 | composeEnhancers(
26 | applyMiddleware(
27 | reduxPromiseListener.middleware,
28 | asyncSubmissionMiddleware,
29 | logger,
30 | ),
31 | ),
32 | );
33 |
34 | export const promiseListener = reduxPromiseListener; // <---------- IMPORTANT
35 |
36 | export default store;
37 |
--------------------------------------------------------------------------------
/examples/async-typeahead-redux/GithubUserTypeahead.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from "react";
2 | import { useSelector, useDispatch } from "react-redux";
3 | import { Field } from "react-final-form";
4 | import { AsyncTypeahead } from "react-bootstrap-typeahead";
5 | import useKeyword from "./useKeyword";
6 | import { compose, propOr, pathOr } from "ramda";
7 | import arrify from "arrify";
8 |
9 | import { searchGithubUsers } from "./actions";
10 |
11 | const AdaptedTypeahead = ({ input, render, meta, ...rest }) => (
12 |
13 | );
14 |
15 | const GithubUserTypeahead = ({ name, ...props }) => {
16 | const { keyword, updateKeyword } = useKeyword(name);
17 |
18 | const dispatch = useDispatch();
19 | const getOptions = useCallback(pathOr([], [keyword, "value"]), [keyword]);
20 | const isLoading = useCallback(pathOr(false, [keyword, "loading"]), [keyword]);
21 | const handleOnSearch = useCallback(compose(dispatch, searchGithubUsers), [
22 | dispatch,
23 | ]);
24 |
25 | const options = useSelector(getOptions);
26 | const loading = useSelector(isLoading);
27 |
28 | return (
29 |
41 | );
42 | };
43 |
44 | export default GithubUserTypeahead;
45 |
--------------------------------------------------------------------------------
/examples/async-typeahead-redux/actions.js:
--------------------------------------------------------------------------------
1 | export const requestGithubUsers = (query) => ({
2 | type: "GITHUB_USERS_REQUEST",
3 | query,
4 | });
5 |
6 | export const storeGithubUsers = (query, users) => ({
7 | type: "GITHUB_USERS_RESPONSE",
8 | query,
9 | users,
10 | });
11 |
12 | export const searchGithubUsers = (query) => (dispatch) => {
13 | dispatch(requestGithubUsers(query));
14 |
15 | fetch(`https://api.github.com/search/users?q=${query}`)
16 | .then((res) => res.json())
17 | .then(({ items: users }) => dispatch(storeGithubUsers(query, users)));
18 | };
19 |
--------------------------------------------------------------------------------
/examples/async-typeahead-redux/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { render } from "react-dom";
3 | import { Provider } from "react-redux";
4 | import Styles from "./Styles";
5 | import { Form } from "react-final-form";
6 | import setFieldData from "final-form-set-field-data";
7 |
8 | import configureStore from "./store";
9 | import GithubUserTypeahead from "./GithubUserTypeahead";
10 |
11 | const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
12 |
13 | const onSubmit = async (values) => {
14 | await sleep(300);
15 | window.alert(JSON.stringify(values, 0, 2));
16 | };
17 |
18 | const store = configureStore();
19 |
20 | const App = () => (
21 |
22 |
32 | By default the format function given to Field is
33 | called every time the component is rendered. But that can lead to a
34 | difficult UX for some types of values. That's why there is a{" "}
35 | formatOnBlur flag that will prevent the format{" "}
36 | function from being called until the field is blurred.
37 |