UNPKG

ra-core

Version:

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

160 lines 7.44 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useCreate = void 0; const react_query_1 = require("@tanstack/react-query"); const useDataProvider_1 = require("./useDataProvider.cjs"); const util_1 = require("../util/index.cjs"); const useMutationWithMutationMode_1 = require("./useMutationWithMutationMode.cjs"); /** * Get a callback to call the dataProvider.create() method, the result and the loading state. * * @param {string} resource * @param {Params} params The create parameters { data } * @param {Object} options Options object to pass to the queryClient. * May include side effects to be executed upon success or failure, e.g. { onSuccess: () => { refresh(); } } * * @typedef Params * @prop params.data The record to create, e.g. { title: 'hello, world' } * * @returns The current mutation state. Destructure as [create, { data, error, isPending }]. * * The return value updates according to the request state: * * - initial: [create, { isPending: false, isIdle: true }] * - start: [create, { isPending: true }] * - success: [create, { data: [data from response], isPending: false, isSuccess: true }] * - error: [create, { error: [error from response], isPending: false, isError: true }] * * The create() function must be called with a resource and a parameter object: create(resource, { data, meta }, options) * * This hook uses react-query useMutation under the hood. * This means the state object contains mutate, isIdle, reset and other react-query methods. * * @see https://tanstack.com/query/v5/docs/react/reference/useMutation * * @example // set params when calling the create callback * * import { useCreate, useRecordContext } from 'react-admin'; * * const LikeButton = () => { * const record = useRecordContext(); * const like = { postId: record.id }; * const [create, { isPending, error }] = useCreate(); * const handleClick = () => { * create('likes', { data: like }) * } * if (error) { return <p>ERROR</p>; } * return <button disabled={isPending} onClick={handleClick}>Like</button>; * }; * * @example // set params when calling the hook * * import { useCreate, useRecordContext } from 'react-admin'; * * const LikeButton = () => { * const record = useRecordContext(); * const like = { postId: record.id }; * const [create, { isPending, error }] = useCreate('likes', { data: like }); * if (error) { return <p>ERROR</p>; } * return <button disabled={isPending} onClick={() => create()}>Like</button>; * }; * * @example // TypeScript * const [create, { data }] = useCreate<Product>('products', { data: product }); * \-- data is Product */ const useCreate = (resource, params = {}, options = {}) => { const dataProvider = (0, useDataProvider_1.useDataProvider)(); const queryClient = (0, react_query_1.useQueryClient)(); const { mutationMode = 'pessimistic', getMutateWithMiddlewares, onSettled, ...mutationOptions } = options; const dataProviderCreate = (0, util_1.useEvent)((resource, params) => dataProvider.create(resource, params)); const [mutate, mutationResult] = (0, useMutationWithMutationMode_1.useMutationWithMutationMode)({ resource, ...params }, { ...mutationOptions, mutationKey: [resource, 'create', params], mutationMode, mutationFn: ({ resource, ...params }) => { if (resource == null) { throw new Error('useCreate mutation requires a resource'); } if (params.data == null) { throw new Error('useCreate mutation requires a non-empty data object'); } return dataProviderCreate(resource, params); }, updateCache: ({ resource, ...params }, { mutationMode }, result) => { const id = mutationMode === 'pessimistic' ? result?.id : params.data?.id; if (!id) { throw new Error('Invalid dataProvider response for create: missing id'); } // hack: only way to tell react-query not to fetch this query for the next 5 seconds // because setQueryData doesn't accept a stale time option const now = Date.now(); const updatedAt = mutationMode === 'undoable' ? now + 5 * 1000 : now; // Stringify and parse the data to remove undefined values. // If we don't do this, an update with { id: undefined } as payload // would remove the id from the record, which no real data provider does. const clonedData = JSON.parse(JSON.stringify(mutationMode === 'pessimistic' ? result : params.data)); queryClient.setQueryData([resource, 'getOne', { id: String(id), meta: params.meta }], (record) => ({ ...record, ...clonedData }), { updatedAt }); return clonedData; }, getQueryKeys: ({ resource, ...params }, { mutationMode }) => { const queryKeys = [ [resource, 'getList'], [resource, 'getInfiniteList'], [resource, 'getMany'], [resource, 'getManyReference'], ]; if (mutationMode !== 'pessimistic' && params.data?.id) { queryKeys.push([ resource, 'getOne', { id: String(params.data.id), meta: params.meta }, ]); } return queryKeys; }, getMutateWithMiddlewares: mutationFn => { if (getMutateWithMiddlewares) { // Immediately get the function with middlewares applied so that even if the middlewares gets unregistered (because of a redirect for instance), // we still have them applied when users have called the mutate function. const mutateWithMiddlewares = getMutateWithMiddlewares(dataProviderCreate.bind(dataProvider)); return args => { // This is necessary to avoid breaking changes in useCreate: // The mutation function must have the same signature as before (resource, params) and not ({ resource, params }) const { resource, ...params } = args; return mutateWithMiddlewares(resource, params); }; } return args => mutationFn(args); }, onUndo: ({ resource, data, meta }) => { queryClient.removeQueries({ queryKey: [ resource, 'getOne', { id: String(data?.id), meta }, ], exact: true, }); }, onSettled: (...args) => { const [, , , mutateResult] = args; // For creation, we always refetch after error or success: mutateResult.snapshot.forEach(([queryKey]) => { queryClient.invalidateQueries({ queryKey }); }); onSettled?.(...args); }, }); const create = (0, util_1.useEvent)((callTimeResource = resource, callTimeParams = {}, callTimeOptions = {}) => { return mutate({ resource: callTimeResource, ...callTimeParams, }, callTimeOptions); }); return [create, mutationResult]; }; exports.useCreate = useCreate; //# sourceMappingURL=useCreate.js.map