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
JavaScript
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