useShowController

useShowController contains the headless logic of the <Show> component. It’s useful to create a custom Show view. It’s also the base hook when building a custom view with another UI kit than Material UI.

useShowController reads the resource name and id from the resource context and browser location, fetches the record from the data provider via dataProvider.getOne(), computes the default page title, and returns them. Its return value matches the ShowContext shape.

useShowController is used internally by <Show> and <ShowBase>. If your Show view uses react-admin components like <TextField>, prefer <ShowBase> to useShowController as it takes care of creating a <ShowContext>.

Usage

You can use useShowController to create your own custom Show view, like this one:

import { useShowController, RecordContextProvider, SimpleShowLayout } from 'react-admin';

const PostShow = () => {
    const { defaultTitle, error, isLoading, record } = useShowController();

    if (isLoading) {
        return <div>Loading...</div>;
    }
    if (error) {
        return <div>Error!</div>;
    }
    return (
        <RecordContextProvider value={record}>
            <h1>{defaultTitle}</h1>
            <SimpleShowLayout>
                <TextField source="title" />
                ...
            </SimpleShowLayout>
        </RecordContextProvider>
    );
};

This custom Show view has no action buttons - it’s up to you to add them in pure React.

Tip: Use <ShowBase> instead of useShowController if you need a component version of that hook.

Parameters

useShowController accepts an object with the following keys, all optional:

disableAuthentication

By default, the useShowController hook will automatically redirect the user to the login page if the user is not authenticated. If you want to disable this behavior and allow anonymous access to a show page, set the disableAuthentication prop to true.

import { useShowController } from 'react-admin';

const PostShow = () => {
    const { record } = useShowController({ disableAuthentication: true });

    return (
        <div>
            <h1>{record.title}</h1>
            <p>{record.body}</p>
        </div>
    );
};

id

By default, useShowController reads the record id from the browser location. But by passing an id prop, you can run the controller logic on an arbitrary record id:

const Post1234Show = () => {
    const { record } = useShowController({ id: 1234 });

    return (
        <div>
            <h1>{record.title}</h1>
            <p>{record.body}</p>
        </div>
    );
};

queryOptions

useShowController accepts a queryOptions prop to pass options to the react-query client.

This can be useful e.g. to override the default error side effect. By default, when the dataProvider.getOne() call fails at the dataProvider level, react-admin shows an error notification and refreshes the page.

You can override this behavior and pass custom side effects by providing a custom queryOptions prop:

import * as React from 'react';
import { useNotify, useRefresh, useRedirect, ShowBase, SimpleShowLayout } from 'react-admin';

const PostShow = props => {
    const notify = useNotify();
    const refresh = useRefresh();
    const redirect = useRedirect();

    const onError = (error) => {
        notify(`Could not load post: ${error.message}`, { type: 'error' });
        redirect('/posts');
        refresh();
    };

    const {
        defaultTitle,
        error,
        isLoading,
        record,
    } = useShowController({ queryOptions: { onError } });

    if (isLoading) {
        return <div>Loading...</div>;
    }
    if (error) {
        return <div>Error!</div>;
    }
    return (
        <RecordContextProvider value={record}>
            <h1>{defaultTitle}</h1>
            <SimpleShowLayout>
                <TextField source="title" />
                ...
            </SimpleShowLayout>
        </RecordContextProvider>
    );
}

The onError function receives the error from the dataProvider call (dataProvider.getOne()), which is a JavaScript Error object (see the dataProvider documentation for details).

The default onError function is:

(error) => {
    notify('ra.notification.item_doesnt_exist', { type: 'error' });
    redirect('list', resource);
    refresh();
}

resource

By default, useShowController reads the resource name from the resource context. But by passing a resource prop, you can run the controller logic on an arbitrary resource:

const PostShow = () => {
    const { record } = useShowController({ resource: 'posts'; id: 1234 });
    return (
        <div>
            <h1>{record.title}</h1>
            <p>{record.body}</p>
        </div>
    );
};

Return Value

useShowController returns an object with the following keys:

const {
    defaultTitle, // Translated title based on the resource, e.g. 'Post #123'
    isFetching, // Boolean, true while the record is being fetched, and false once done fetching
    isLoading, // Boolean, true until the record is available for the first time
    record, // Either the record fetched via dataProvider.getOne() based on the id from the location, a cached version of the record (see also the Caching documentation page) or undefined 
    refetch, // Callback to refetch the record via dataProvider.getOne()
    resource, // The resource name, deduced from the location. e.g. 'posts'
    error, // Error returned by dataProvider when it failed to fetch the record. Useful if you want to adapt the view instead of just showing a notification using the onError side effect.
} = useShowController();

Controlled Mode

By default, useShowController reads the resource name from the resource context, and the record id from the browser location.

But by passing resource and id props, you can run the controller logic outside these contexts:

import { useShowController } from 'react-admin';
import ShowView from './ShowView';

const MyShow = () => {
    const controllerProps = useShowController({ resource: 'posts', id: 1234 });
    return <ShowView {...controllerProps} />;
};