diff --git a/src/client/components/elements/DuplicateDocument/index.js b/src/client/components/elements/DuplicateDocument/index.js index 40c27aafba..6a1ed11b65 100644 --- a/src/client/components/elements/DuplicateDocument/index.js +++ b/src/client/components/elements/DuplicateDocument/index.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { useHistory } from 'react-router-dom'; import config from 'payload/config'; import Button from '../Button'; -import useForm from '../../forms/Form/useForm'; +import { useForm } from '../../forms/Form/context'; import './index.scss'; diff --git a/src/client/components/elements/PreviewButton/index.js b/src/client/components/elements/PreviewButton/index.js index 8c2c8f3701..3a444dd82b 100644 --- a/src/client/components/elements/PreviewButton/index.js +++ b/src/client/components/elements/PreviewButton/index.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import useForm from '../../forms/Form/useForm'; +import { useForm } from '../../forms/Form/context'; import { useUser } from '../../data/User'; import Button from '../Button'; diff --git a/src/client/components/forms/Form/FieldContext.js b/src/client/components/forms/Form/FieldContext.js deleted file mode 100644 index 7c0127fbd6..0000000000 --- a/src/client/components/forms/Form/FieldContext.js +++ /dev/null @@ -1,3 +0,0 @@ -import { createContext } from 'react'; - -export default createContext({}); diff --git a/src/client/components/forms/Form/FormContext.js b/src/client/components/forms/Form/FormContext.js deleted file mode 100644 index 7c0127fbd6..0000000000 --- a/src/client/components/forms/Form/FormContext.js +++ /dev/null @@ -1,3 +0,0 @@ -import { createContext } from 'react'; - -export default createContext({}); diff --git a/src/client/components/forms/Form/context.js b/src/client/components/forms/Form/context.js new file mode 100644 index 0000000000..efacd8df45 --- /dev/null +++ b/src/client/components/forms/Form/context.js @@ -0,0 +1,26 @@ +import { createContext, useContext } from 'react'; + +const FormContext = createContext({}); +const FieldContext = createContext({}); +const SubmittedContext = createContext(false); +const ProcessingContext = createContext(false); +const ModifiedContext = createContext(false); + +const useForm = () => useContext(FormContext); +const useFormFields = () => useContext(FieldContext); +const useFormSubmitted = () => useContext(SubmittedContext); +const useFormProcessing = () => useContext(ProcessingContext); +const useFormModified = () => useContext(ModifiedContext); + +export { + FormContext, + FieldContext, + SubmittedContext, + ProcessingContext, + ModifiedContext, + useForm, + useFormFields, + useFormSubmitted, + useFormProcessing, + useFormModified, +}; diff --git a/src/client/components/forms/Form/index.js b/src/client/components/forms/Form/index.js index 36846885ad..a4cba6417a 100644 --- a/src/client/components/forms/Form/index.js +++ b/src/client/components/forms/Form/index.js @@ -1,13 +1,11 @@ import React, { - useReducer, useEffect, useRef, + useReducer, useEffect, useRef, useState, } from 'react'; import { objectToFormData } from 'object-to-formdata'; import { useHistory } from 'react-router-dom'; import PropTypes from 'prop-types'; import { unflatten } from 'flatley'; import HiddenInput from '../field-types/HiddenInput'; -import FormContext from './FormContext'; -import FieldContext from './FieldContext'; import { useLocale } from '../../utilities/Locale'; import { useStatusList } from '../../elements/Status'; import { requests } from '../../../api'; @@ -16,6 +14,8 @@ import { useUser } from '../../data/User'; import fieldReducer from './fieldReducer'; import initContextState from './initContextState'; +import { SubmittedContext, ProcessingContext, ModifiedContext, FormContext, FieldContext } from './context'; + import './index.scss'; const baseClass = 'form'; @@ -43,7 +43,7 @@ const Form = (props) => { ajax, method, action, - handleAjaxResponse, + handleResponse, onSuccess, children, className, @@ -56,6 +56,10 @@ const Form = (props) => { const { replaceStatus, addStatus, clearStatus } = useStatusList(); const { refreshCookie } = useUser(); + const [modified, setModified] = useState(false); + const [processing, setProcessing] = useState(false); + const [submitted, setSubmitted] = useState(false); + const contextRef = useRef({ ...initContextState }); const [fields, dispatchFields] = useReducer(fieldReducer, {}); @@ -69,7 +73,7 @@ const Form = (props) => { } e.stopPropagation(); - contextRef.current.setSubmitted(true); + setSubmitted(true); const isValid = contextRef.current.validateForm(); @@ -106,17 +110,17 @@ const Form = (props) => { }); const formData = contextRef.current.createFormData(); - contextRef.current.setProcessing(true); + setProcessing(true); // Make the API call from the action return requests[method.toLowerCase()](action, { body: formData, }).then((res) => { - contextRef.current.setModified(false); - if (typeof handleAjaxResponse === 'function') return handleAjaxResponse(res); + setModified(false); + if (typeof handleResponse === 'function') return handleResponse(res); return res.json().then((json) => { - contextRef.current.setProcessing(false); + setProcessing(false); clearStatus(); if (res.status < 400) { @@ -144,9 +148,7 @@ const Form = (props) => { } if (Array.isArray(json.errors)) { - const [fieldErrors, nonFieldErrors] = json.errors.reduce(([fieldErrs, nonFieldErrs], err) => { - return err.field && err.message ? [[...fieldErrs, err], nonFieldErrs] : [fieldErrs, [...nonFieldErrs, err]]; - }, [[], []]); + const [fieldErrors, nonFieldErrors] = json.errors.reduce(([fieldErrs, nonFieldErrs], err) => (err.field && err.message ? [[...fieldErrs, err], nonFieldErrs] : [fieldErrs, [...nonFieldErrs, err]]), [[], []]); fieldErrors.forEach((err) => { dispatchFields({ @@ -193,17 +195,11 @@ const Form = (props) => { return true; }; - contextRef.current.getFields = () => { - return contextRef.current.fields; - }; + contextRef.current.getFields = () => contextRef.current.fields; - contextRef.current.getField = (path) => { - return contextRef.current.fields[path]; - }; + contextRef.current.getField = (path) => contextRef.current.fields[path]; - contextRef.current.getData = () => { - return reduceFieldsToValues(contextRef.current.fields, true); - }; + contextRef.current.getData = () => reduceFieldsToValues(contextRef.current.fields, true); contextRef.current.getSiblingData = (path) => { let siblingFields = contextRef.current.fields; @@ -247,40 +243,26 @@ const Form = (props) => { return unflattenedData?.[name]; }; - contextRef.current.getUnflattenedValues = () => { - return reduceFieldsToValues(contextRef.current.fields); - }; + contextRef.current.getUnflattenedValues = () => reduceFieldsToValues(contextRef.current.fields); - contextRef.current.validateForm = () => { - return !Object.values(contextRef.current.fields).some((field) => { - return field.valid === false; - }); - }; + contextRef.current.validateForm = () => !Object.values(contextRef.current.fields).some((field) => field.valid === false); contextRef.current.createFormData = () => { const data = reduceFieldsToValues(contextRef.current.fields); return objectToFormData(data, { indices: true }); }; - contextRef.current.setModified = (modified) => { - contextRef.current.modified = modified; - }; - - contextRef.current.setSubmitted = (submitted) => { - contextRef.current.submitted = submitted; - }; - - contextRef.current.setProcessing = (processing) => { - contextRef.current.processing = processing; - }; + contextRef.current.setModified = setModified; + contextRef.current.setProcessing = setProcessing; + contextRef.current.setSubmitted = setSubmitted; useThrottledEffect(() => { refreshCookie(); }, 15000, [fields]); useEffect(() => { - contextRef.current.modified = false; - }, [locale, contextRef.current.modified]); + setModified(false); + }, [locale]); const classes = [ className, @@ -301,13 +283,20 @@ const Form = (props) => { ...contextRef.current, }} > - - {children} + + + + + {children} + + + + ); }; @@ -318,7 +307,7 @@ Form.defaultProps = { ajax: true, method: 'POST', action: '', - handleAjaxResponse: null, + handleResponse: null, onSuccess: null, className: '', disableSuccessStatus: false, @@ -331,7 +320,7 @@ Form.propTypes = { ajax: PropTypes.bool, method: PropTypes.oneOf(['post', 'POST', 'get', 'GET', 'put', 'PUT', 'delete', 'DELETE']), action: PropTypes.string, - handleAjaxResponse: PropTypes.func, + handleResponse: PropTypes.func, onSuccess: PropTypes.func, children: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.node), diff --git a/src/client/components/forms/Form/initContextState.js b/src/client/components/forms/Form/initContextState.js index 9ae8d322d9..ff449921b6 100644 --- a/src/client/components/forms/Form/initContextState.js +++ b/src/client/components/forms/Form/initContextState.js @@ -1,7 +1,4 @@ export default { - processing: false, - modified: false, - submitted: false, getFields: () => { }, getField: () => { }, getData: () => { }, diff --git a/src/client/components/forms/Form/useForm.js b/src/client/components/forms/Form/useForm.js deleted file mode 100644 index 6d968ee4ec..0000000000 --- a/src/client/components/forms/Form/useForm.js +++ /dev/null @@ -1,4 +0,0 @@ -import { useContext } from 'react'; -import FormContext from './FormContext'; - -export default () => useContext(FormContext); diff --git a/src/client/components/forms/Form/useFormFields.js b/src/client/components/forms/Form/useFormFields.js deleted file mode 100644 index 749ef7627f..0000000000 --- a/src/client/components/forms/Form/useFormFields.js +++ /dev/null @@ -1,4 +0,0 @@ -import { useContext } from 'react'; -import FieldContext from './FieldContext'; - -export default () => useContext(FieldContext); diff --git a/src/client/components/forms/Submit/index.js b/src/client/components/forms/Submit/index.js index 85b11ce3bb..7dc4833be1 100644 --- a/src/client/components/forms/Submit/index.js +++ b/src/client/components/forms/Submit/index.js @@ -1,6 +1,6 @@ -import React, { useContext } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; -import FormContext from '../Form/FormContext'; +import { useFormProcessing } from '../Form/context'; import Button from '../../elements/Button'; import './index.scss'; @@ -8,12 +8,13 @@ import './index.scss'; const baseClass = 'form-submit'; const FormSubmit = ({ children }) => { - const formContext = useContext(FormContext); + const processing = useFormProcessing(); + return (
diff --git a/src/client/components/forms/field-types/Auth/APIKey.js b/src/client/components/forms/field-types/Auth/APIKey.js index 3b2461784c..706740a9b7 100644 --- a/src/client/components/forms/field-types/Auth/APIKey.js +++ b/src/client/components/forms/field-types/Auth/APIKey.js @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React, { useMemo, useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import { v4 as uuidv4 } from 'uuid'; import useFieldType from '../../useFieldType'; @@ -6,19 +6,21 @@ import Label from '../../Label'; import Button from '../../../elements/Button'; import CopyToClipboard from '../../../elements/CopyToClipboard'; import { text } from '../../../../../fields/validations'; -import useFormFields from '../../Form/useFormFields'; +import { useFormFields } from '../../Form/context'; import './index.scss'; const path = 'apiKey'; const baseClass = 'api-key'; -const validate = val => text(val, { minLength: 24, maxLength: 48 }); +const validate = (val) => text(val, { minLength: 24, maxLength: 48 }); const APIKey = (props) => { const { initialData, } = props; + const [initialAPIKey, setInitialAPIKey] = useState(null); + const { getField } = useFormFields(); const apiKey = getField(path); @@ -36,7 +38,7 @@ const APIKey = (props) => { const fieldType = useFieldType({ path: 'apiKey', - initialData: initialData || uuidv4(), + initialData: initialData || initialAPIKey, validate, }); @@ -45,6 +47,10 @@ const APIKey = (props) => { setValue, } = fieldType; + useEffect(() => { + setInitialAPIKey(uuidv4()); + }, []); + const classes = [ 'field-type', 'api-key', @@ -52,7 +58,7 @@ const APIKey = (props) => { ].filter(Boolean).join(' '); return ( - <> +