useRecordContext

useRecordContext grabs the current record. It’s available anywhere react-admin manipulates a record, e.g. in a Show page, in a Datagrid row, or in a Reference Field.

Usage

The most common use case for useRecordContext is to build a custom field. For instance, an author field for a book Show view.

import { useRecordContext, Show, SimpleShowLayout } from 'react-admin';

const BookAuthor = () => {
    const record = useRecordContext();
    if (!record) return null;
    return <span>{record.author}</span>;
};

const BookShow = () => (
    <Show>
        <SimpleShowLayout>
            <BookAuthor />
            ...
        </SimpleShowLayout>
    </Show>
)

Optimistic Rendering

As react-admin uses optimistic rendering, useRecordContext may return an undefined record on load. Make sure you prepare for that!

const BookAuthor = () => {
    const record = useRecordContext();
    // warning: this will fail on load since record is undefined    
    return <span>{record.author}</span>;
};

So make sure you check that the record is defined before using it.

const record = useRecordContext();
if (!record) return null;

Availability

As soon as there is a record available, react-admin puts it in a RecordContext. This means that useRecordContext works out of the box:

  • in descendants of the <Show> and <ShowBase> component
  • in descendants of the <Edit> and <EditBase> component
  • in descendants of the <Create> and <CreateBase> component
  • in descendants of the <Datagrid> component
  • in descendants of the <SimpleList> component
  • in descendants of the <ReferenceField> component

Inside A Form

Inside <Edit> and <Create>, useRecordContext returns the initial record, used to set the initial form values.

If you want to react to the data entered by the user, use react-hook-form’s useWatch instead of useRecordContext. It returns the current form values, including the changes made by the user.

For instance if you want to display an additional input when a user marks an order as returned, you can do the following:

import { Edit, SimpleForm, BooleanInput, TextInput } from 'react-admin';
import { useWatch } from 'react-hook-form';

const ReturnedReason = () => {
    const isReturned = useWatch({ name: 'returned' });
    return isReturned ? <TextInput source="reason" /> : null;
};

const OrderEdit = () => (
    <Edit>
        <SimpleForm>
            <TextInput source="reference" />
            <BooleanInput source="returned" />
            <ReturnedReason />
            ...
        </SimpleForm>
    </Edit>
)

Creating a Record Context

If you have fetched a record and you want to make it available to descendants, place it inside a <RecordContextProvider> component.

import { useGetOne, RecordContextProvider } from 'react-admin';

const RecordFetcher = ({ id, resource, children }) => {
    const { data, isLoading, error } = useGetOne(resource, { id });
    if (isLoading) return <p>Loading...</p>;
    if (error) return <p>Error :(</p>;
    return (
        <RecordContextProvider value={data}>
            {children}
        </RecordContextProvider>
    );
};

Fallback Mode

Some react-admin components accept an optional record prop. These components can be used both inside a RecordContext, and with a custom record prop - without creating a custom record context.

You can do the same: just accept a record component prop, and pass the props as parameter to the hook. If the record is undefined, useRecordContext will return the record from the context. If it is defined, useRecordContext will return the record from the props.

const BookAuthor = (props) => {
    const record = useRecordContext(props);
    if (!record) return null;
    return <span>{record.author}</span>;
};

// you can now pass a custom record
<BookAuthor record={{ id: 123, author: 'Leo Tolstoy' }}>

TypeScript

The useRecordContext hook accepts a generic parameter for the record type:

type Book = {
    id: number;
    author: string;
};

const BookAuthor = () => {
    const book = useRecordContext<Book>();
    if (!book) return null;
    // TypeScript knows that book is of type Book
    return <span>{book.author}</span>;
};

See Also

API