Skip to content

useRegisterMutationMiddleware

Ra-core lets you hook into the save logic of the forms in Creation and Edition pages using middleware functions. These functions “wrap” the main mutation (dataProvider.create() in a Creation page, dataProvider.update() in an Edition page), so you can add your own code to be executed before and after it. This allows you to perform various advanced form use cases, such as:

  • transforming the data passed to the main mutation,
  • updating the mutation parameters before it is called,
  • creating, updating or deleting related data,
  • adding performances logs,
  • etc.

Middleware functions have access to the same parameters as the underlying mutation (create or update), and to a next function to call the next function in the mutation lifecycle.

useRegisterMutationMiddleware allows to register a mutation middleware function for the current form.

Define a middleware function, then use the hook to register it.

For example, a middleware for the create mutation looks like the following:

import * as React from 'react';
import {
useRegisterMutationMiddleware,
CreateParams,
CreateMutationFunction
} from 'ra-core';
const MyComponent = () => {
const createMiddleware = async (
resource: string,
params: CreateParams,
next: CreateMutationFunction
) => {
// Do something before the mutation
// Call the next middleware
const result = await next(resource, params);
// Do something after the mutation
// Always return the result
return result;
}
const memoizedMiddleWare = React.useCallback(createMiddleware, []);
useRegisterMutationMiddleware(memoizedMiddleWare);
// ...
}

Then, render that component as a descendent of the page controller component (<CreateBase> or <EditBase>).

Ra-core will wrap each call to the dataProvider.create() mutation with the createMiddleware function as long as the MyComponent component is mounted.

useRegisterMutationMiddleware unregisters the middleware function when the component unmounts. For this to work correctly, you must provide a stable reference to the function by wrapping it in a useCallback hook for instance.

useRegisterMutationMiddleware expects a single argument: a middleware function.

A middleware function must have the following signature:

const middlware = async (resource, params, next) => {
// Do something before the mutation
// Call the next middleware
const result = await next(resource, params);
// Do something after the mutation
// Always return the result
return result;
}

The params type depends on the mutation:

  • For a create middleware, { data, meta }
  • For an update middleware, { id, data, previousData }

The following example shows a custom <ImageInput> that converts its images to base64 on submit, and updates the main resource record to use the base64 versions of those images:

import { useCallback } from 'react';
import {
CreateMutationFunction,
Middleware,
useRegisterMutationMiddleware
} from 'ra-core';
import { ImageInput } from '../components';
const ThumbnailInput = () => {
const middleware = useCallback(async (
resource,
params,
next
) => {
const b64 = await convertFileToBase64(params.data.thumbnail);
// Update the parameters that will be sent to the dataProvider call
const newParams = { ...params, data: { ...data, thumbnail: b64 } };
return next(resource, newParams);
}, []);
useRegisterMutationMiddleware(middleware);
return <ImageInput source="thumbnail" />;
};
const convertFileToBase64 = (file: {
rawFile: File;
src: string;
title: string;
}) =>
new Promise((resolve, reject) => {
// If the file src is a blob url, it must be converted to b64.
if (file.src.startsWith('blob:')) {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file.rawFile);
} else {
resolve(file.src);
}
});

Use the <ThumbnailInput> component in a creation form just like any regular Input component:

const PostCreate = () => (
<CreateBase>
<Form>
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
<TextInput source="title" />
<TextInput source="body" multiline />
<ThumbnailInput />
</div>
</Form>
</CreateBase>
);

With this middleware, given the following form values:

{
"data": {
"thumbnail": {
"rawFile": {
"path": "avatar.jpg"
},
"src": "blob:http://localhost:9010/c925dc18-5918-4782-8087-b2464896b8f9",
"title": "avatar.jpg"
}
}
}

The dataProvider create function will be called with:

{
"data": {
"thumbnail": {
"title":"avatar.jpg",
"src":"data:image/jpeg;base64,..."
}
}
}