UNPKG

@base-ui/react

Version:

Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.

125 lines (124 loc) 3.73 kB
'use client'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { useIsoLayoutEffect } from '@base-ui/utils/useIsoLayoutEffect'; import { useStableCallback } from '@base-ui/utils/useStableCallback'; import { getCombinedFieldValidityData } from "../../field/utils/getCombinedFieldValidityData.js"; import { useFormContext } from "../form-context/FormContext.js"; export function useFieldControlRegistration(params) { const { commit, invalid, markedDirtyRef, name, setValidityData, validityData } = params; const { formRef } = useFormContext(); const activeFieldControlSourceRef = React.useRef(null); const registrationRef = React.useRef(null); const fallbackControlRef = React.useRef(null); const getValue = useStableCallback(() => { const registration = registrationRef.current; if (!registration) { return undefined; } if (registration.getValue) { return registration.getValue(); } return registration.value; }); const validate = useStableCallback((flushSync = true) => { const registration = registrationRef.current; if (!registration) { return; } let nextValue = registration.value; if (nextValue === undefined) { nextValue = getValue(); } markedDirtyRef.current = true; if (!flushSync) { commit(nextValue); } else { // Synchronously update the validity state so the submit event can be prevented. ReactDOM.flushSync(() => commit(nextValue)); } }); function refreshRegistration() { const registration = registrationRef.current; if (!registration || !registration.id) { return; } formRef.current.fields.set(registration.id, { getValue, name, controlRef: registration.controlRef ?? fallbackControlRef, validityData: getCombinedFieldValidityData(validityData, invalid), validate }); } function deleteRegistration(id = registrationRef.current?.id) { if (id) { formRef.current.fields.delete(id); } } function syncInitialValue() { const registration = registrationRef.current; if (!registration) { return; } let initialValue = registration.value; if (initialValue === undefined) { initialValue = getValue(); } if (validityData.initialValue === null && initialValue !== null) { setValidityData(prev => ({ ...prev, initialValue })); } } useIsoLayoutEffect(() => { const registration = registrationRef.current; if (!registration || !registration.id) { return; } formRef.current.fields.set(registration.id, { getValue, name, controlRef: registration.controlRef ?? fallbackControlRef, validityData: getCombinedFieldValidityData(validityData, invalid), validate }); }, [formRef, getValue, invalid, name, validate, validityData]); useIsoLayoutEffect(() => { const fields = formRef.current.fields; return () => { const id = registrationRef.current?.id; if (id) { fields.delete(id); } }; }, [formRef]); return useStableCallback((source, registration) => { if (!registration) { if (activeFieldControlSourceRef.current === source) { activeFieldControlSourceRef.current = null; deleteRegistration(); registrationRef.current = null; } return; } const previousId = registrationRef.current?.id; activeFieldControlSourceRef.current = source; registrationRef.current = registration; if (previousId && previousId !== registration.id) { deleteRegistration(previousId); } syncInitialValue(); refreshRegistration(); }); }