UNPKG

ra-core

Version:

Core components of react-admin, a frontend Framework for building admin applications on top of REST services, using ES6, React

108 lines 4.19 kB
import { useCallback, useRef } from 'react'; import merge from 'lodash/merge.js'; import set from 'lodash/set.js'; import { useResourceContext } from "../../core/index.js"; import { useDataProvider } from "../../dataProvider/index.js"; import { useTranslate, useTranslateLabel } from "../../i18n/index.js"; import { asyncDebounce } from "../../util/index.js"; import { useRecordContext } from "../../controller/index.js"; import { isEmpty } from "./validate.js"; /** * A hook that returns a validation function checking for a record field uniqueness * by calling the dataProvider getList function with a filter. * * @example // Passing options at declaration time * const UserCreateForm = () => { * const unique = useUnique({ message: 'Username is already used'}); * return ( * <SimpleForm> * <TextInput source="username" validate={unique()} /> * </SimpleForm> * ); * } * * @example // Passing options at call time * const UserCreateForm = () => { * const unique = useUnique(); * return ( * <SimpleForm> * <TextInput source="username" validate={unique({ message: 'Username is already used'})} /> * </SimpleForm> * ); * } * * @example // With additional filters * const UserCreateForm = () => { * const unique = useUnique(); * return ( * <SimpleForm> * <ReferenceInput source="organization_id" reference="organizations" /> * <FormDataConsumer> * {({ formData }) => ( * <TextInput * source="username" * validate={unique({ filter: { organization_id: formData.organization_id })} * /> * )} * </FormDataConsumer> * </SimpleForm> * ); * } */ export const useUnique = (options) => { const dataProvider = useDataProvider(); const translateLabel = useTranslateLabel(); const resource = useResourceContext(options); if (!resource) { throw new Error('useUnique: missing resource prop or context'); } const translate = useTranslate(); const record = useRecordContext(); const debouncedGetList = useRef( // The initial value is here to set the correct type on useRef asyncDebounce(dataProvider.getList, options?.debounce ?? DEFAULT_DEBOUNCE)); const validateUnique = useCallback((callTimeOptions) => { const { message, filter, debounce: interval, } = merge({ debounce: DEFAULT_DEBOUNCE, filter: {}, message: 'ra.validation.unique', }, options, callTimeOptions); debouncedGetList.current = asyncDebounce(dataProvider.getList, interval); return async (value, allValues, props) => { if (isEmpty(value)) { return undefined; } try { const finalFilter = set(merge({}, filter), props.source, value); const { data, total } = await debouncedGetList.current(resource, { filter: finalFilter, pagination: { page: 1, perPage: 1 }, sort: { field: 'id', order: 'ASC' }, }); if (typeof total !== 'undefined' && total > 0 && !data.some(r => r.id === record?.id)) { return { message, args: { source: props.source, value, field: translateLabel({ label: props.label, source: props.source, resource, }), }, }; } } catch (error) { return translate('ra.notification.http_error'); } return undefined; }; }, [dataProvider, options, record, resource, translate, translateLabel]); return validateUnique; }; const DEFAULT_DEBOUNCE = 1000; //# sourceMappingURL=useUnique.js.map