UNPKG

@shopify/react-form

Version:

Manage React forms tersely and safely-typed with no magic using React hooks

129 lines (123 loc) 5.24 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var react = require('react'); var reactHooks = require('@shopify/react-hooks'); var utilities = require('../utilities.js'); var dirty = require('./dirty.js'); var reset = require('./reset.js'); var submit = require('./submit.js'); var dynamiclistdirty = require('./list/dynamiclistdirty.js'); var dynamiclistreset = require('./list/dynamiclistreset.js'); /** * A custom hook for managing the state of an entire form. `useForm` wraps up many of the other hooks in this package in one API, and when combined with `useField`, `useList` or `useDynamicList`, allows you to easily build complex forms with smart defaults for common cases. * * ### Examples * *```typescript * import React from 'react'; * import {useField, useForm} from '@shopify/react-form'; * * function MyComponent() { * const { fields, submit, submitting, dirty, reset, submitErrors } = useForm({ * fields: { * title: useField('some default title'), * }, * onSubmit: (fieldValues) => { * return {status: "fail", errors: [{message: 'bad form data'}]} * } * }); * * return ( * <form onSubmit={submit}> * {submitting && <p className="loading">loading...</p>} * {submitErrors.length>0 && <p className="error">submitErrors.join(', ')</p>} * <div> * <label for="title">Title</label> * <input * id="title" * name="title" * value={title.value} * onChange={title.onChange} * onBlur={title.onBlur} * /> * {title.error && <p className="error">{title.error}</p>} * </div> * <button disabled={!dirty} onClick={reset}>Reset</button> * <button type="submit" disabled={!dirty}>Submit</button> * </form> * ); *``` * * @param fields - A dictionary of `Field` objects, dictionaries of `Field` objects, and lists of dictionaries of `Field` objects. Generally, you'll want these to be generated by the other hooks in this package, either `useField` or `useList`. This will be returned back out as the `fields` property of the return value. * * @param onSubmit - An async function to handle submission of the form. If this function returns an object of `{status: 'fail', error: FormError[]}` then the submission is considered a failure. Otherwise, it should return an object with `{status: 'success'}` and the submission will be considered a success. `useForm` will also call all client-side validation methods for the fields passed to it. The `onSubmit` handler will not be called if client validations fails. * @param dynamicLists - optional dictionaries of `DynamicList`. * @param makeCleanAfterSubmit * @returns An object representing the current state of the form, with imperative methods to reset, submit, validate, and clean. Generally, the returned properties correspond 1:1 with the specific hook/utility for their functionality. * * @remarks * **Building your own:** Internally, `useForm` is a convenience wrapper over `useDirty`, `useReset`, and `useSubmit`. If you only need some of its functionality, consider building a custom hook combining a subset of them. * **Subforms:** You can have multiple `useForm`s wrapping different subsets of a group of fields. Using this you can submit subsections of the form independently and have all the error and dirty tracking logic "just work" together. */ function useForm({ fields, dynamicLists, onSubmit, makeCleanAfterSubmit = false }) { const fieldsWithLists = react.useMemo(() => { if (dynamicLists) { const fieldsWithList = { ...fields }; Object.entries(dynamicLists).forEach(([key, value]) => { fieldsWithList[key] = value.fields; }); return fieldsWithList; } return fields; }, [dynamicLists, fields]); const dirty$1 = dirty.useDirty(fieldsWithLists); const basicReset = reset.useReset(fieldsWithLists); const dynamicListDirty = dynamiclistdirty.useDynamicListDirty(dynamicLists); const dynamicListReset = dynamiclistreset.useDynamicListReset(dynamicLists); const { submit: submit$1, submitting, errors, setErrors } = submit.useSubmit(onSubmit, fieldsWithLists, makeCleanAfterSubmit, dynamicLists); const reset$1 = react.useCallback(() => { setErrors([]); basicReset(); dynamicListReset(); }, [basicReset, dynamicListReset, setErrors]); const fieldsRef = reactHooks.useLazyRef(() => fieldsWithLists); fieldsRef.current = fieldsWithLists; const dynamicListsRef = reactHooks.useLazyRef(() => dynamicLists); const validate = react.useCallback(() => { return utilities.validateAll(fieldsRef.current); }, [fieldsRef]); const makeClean = react.useCallback(() => { utilities.makeCleanFields(fieldsRef.current); utilities.makeCleanDynamicLists(dynamicListsRef.current); }, [dynamicListsRef, fieldsRef]); const form = { fields, dirty: dirty$1 || dynamicListDirty, submitting, submit: submit$1, reset: reset$1, validate, makeClean, submitErrors: errors }; if (dynamicLists) { return { ...form, dynamicLists }; } return form; } exports.useForm = useForm;