React-Admin April 2021 Update

François Zaninotto
François ZaninottoApril 07, 2021
#react#react-admin

It's only been two months since our latest update about react-admin, but many new features have landed in the latest two versions. If you haven't followed the regular updates from our @ReactAdmin Twitter account, now is a good time to catch up about the changes in react-admin 3.13 to 3.14.

For a complete changelog, check the react-admin releases page.

UX Improvements

With each release, we polish the interface to make the user experience more delightful. We don't try to invent interface conventions. Instead, we do our best to let users use the same interactions as they do in other software.

  • Add support for selecting a range of <Datagrid> rows by using shift + click
  • Add scroll to top on key navigation links
  • Improve <AutocompleteArrayInput> filtering performance
  • Fix <AutocompleteInput> and <AutocompletearrayInput> options appear behind Dialog (long-standing issue)

Select rows

New Features

React-admin 3.13 and 3.14 introduce two major features and many smaller ones.

Dependent Queries

Developers love the dataProvider query hooks (useQuery, useQueryWithStore, useGetOne, useGetList, useGetMany, etc.). These hooks offer an intuitive way to handle loading, empty, and error states without pain.

But when a page needs to make several of these calls in a row, one query depending on the result of the other, developers used to have to call the dataProvider directly.

With the introduction of the enabled option, react-admin can skip queries altogether when their dependencies aren't ready yet:

// fetch posts
const { ids, data: posts, loading: isLoading } = useGetList<Post>(
    'posts',
    { page: 1, perPage: 20 },
    { field: 'name', order: 'ASC' },
    {}
);

// then fetch categories for these posts
const { data: categories, loading: isLoading } = useGetMany<Category>(
    'categories',
    ids.map(id=> posts[id].category_id),
    // run only if the first query returns non-empty result
    { enabled: ids.length > 0 }
);

useRecordContext

React-admin now creates a context everywhere there is a record available. For instance, in an <Edit> or <Show> view, in a <ReferenceField>, in <Datagrid> rows, or in <SimpleList> items. You can grab the record in descendent components by calling useRecordContext().

This greatly simplifies the creation of custom Show layouts, custom fields, and custom sidebars.

For instance, to display an avatar based on a record field, you no longer need to worry about whether the record is injected or not. Just pull it from the context:

import { Avatar } from '@material-ui/core';
import { useRecordContext } from 'react-admin';

export const AvatarField = () => {
    const record = useRecordContext();
    if (!record) return null;
    return (
        <Avatar src={record.avatar}>
            {record.first_name.charAt(0)}
            {record.last_name.charAt(0)}
        </Avatar>
    );
}

You can use this Field with no props, e.g. in a Show page:

const ContactShow = () => (
    <Box display="flex">
        <AvatarField />
        <Box flex="1">
            <TextField source="name" component="p"/>
            <TextField source="profession" component="p"/>
        </Box>
    </Box>
);

Tip: React-admin creates a RecordContext where there can be a record, but during the loading phase, the record isn't defined yet. Don't forget to test for existence!

All Field components now use useRecordContext, so you don't need to pass an explicit record prop when inside a RecordContext (i.e. virtually everywhere).

We're relying more and more on contexts, in addition to child cloning and props injection. We've understood that props injection is confusing for developers who don't understand where the props come from and when they are injected or not. In the future major version, we'll remove child cloning and rely exclusively on contexts.

Other New Features

Many individual components gained new capabilities in the recent releases:

  • Add <Datagrid isRowExpandable> prop to control when to allow users to expand rows
  • Add support for <Labeled fullWidth> for better Show layouts
  • Add support for <ArrayInput helperText>
  • Add success notification type, to show a green toast message when users need to know their action ended successfully
  • Add ability to display disabled options in <SelectArrayInput>
  • Add support for array values in <ReferenceArrayField> filter
  • Add ability to disable routing in <TabbedForm> and <TabbedShowLayout> (and to use more than one such layout per page)
  • <Admin customRoutes> with noLayout now render even though there is no <Resource> - useful for fined-grained permissions
  • <ReferenceArrayInput> now creates a ListContext, which allows using regular iterators (like <Datagrid>) as child
  • Add useGetResourceLabel hook to get the (translated) page name for a resource

Developer Experience Improvements

We've been working on the react-admin code for more than 4 years, and we know all its intricacies. Maybe a little too much. When React developers discover react-admin, they usually hit many small bumps that make the developer experience a bit painful.

We've put ourselves in such shoes by building new react-admin projects from scratch. We've also interviewed junior developers who learn both React and react-admin at the same time to understand their pain points.

The feedback we got helped us make the developer experience smoother by removing many little frictions. Here is the list.

Less Boilerplate / WTF

These changes reduce the amount of code you need to write to develop a custom component. For example, a custom Aside component for a contact Show view:

-const ContactAside = ({ record }) => {
+const ContactAside = () => {
    return (
        <div>
            <h3>Age</h3>
-           <NumberField record={record} source="age" />
+           <NumberField source="age" />


            <h3>First Seen</h3>
-           <DateField record={record} source="created_at" />
+           <DateField source="created_at" />

            <h3>Company</h3>
            <ReferenceField
-             record={record}
-             basePath="/contacts"
              source="company_id"
              reference="companies"
            >
                <TextField source="name">
            </ReferenceManyField>

-           <EditButton basePath="/contacts" record={record} />
+           <EditButton />
        </div>
    );
}
  • Add ability to omit record and basePath in Buttons and Reference Fields when building a custom page layout
  • Add ability to call useGetList without pagination, sort, or filter params
  • You can now use <List> as a standalone component, in which case it doesn't sync the sorting, pagination, and filter parameters with the URL
  • Improve <Datagrid> and <SimpleList> defaults to make them less verbose in the general case
  • Remove translation warnings when an admin doesn't use any i18nProvider
  • Remove translation warnings about error messages in the <Error> component
  • Fix TypeScript types preventing simple usage of useGetList , <SimpleList> and <Datagrid>

Easier Overrides

We've been through this so many times: a react-admin component seems to do the job for one particular requirement. Unfortunately, the customer has one specific demand about labels, styles, or layout that isn't supported by the react-admin component. So you end up copying the component code into your project and change only one line.

This is what we want to avoid by providing more ways to override defaults.

  • Add support for a React element as <Confirm content> prop value
  • Add support for <SingleFieldList component>
  • Add ability to use an element as the label in <FormTab>
  • Add ability to use an element as the label in <FilterListItem>
  • Add ability to override the <UserMenu> component style

So now, if you want to use a <SingleFieldList> inside a <p> element without warning about bad DOM hierarchy, no need to rewrite it from scratch: just pass <SingleFieldList component="span">!

Improved Docs & Types

One of our goals is to minimize the time a developer spends on the documentation. We can always improve APIs to make them more intuitive, but throughout the past 2 releases, we've found other ways to do so.

  • Improve documentation search and add keyboard shortcut (Ctrl + K)
  • Bootstrap new doc chapter about buttons
  • "Go to definition" on a react-admin component now goes to the source (uncompiled) code in VSCode
  • Improve TypeScript types for Inputs and dataProvider hooks

improved Search

Internal Changes

We've moved the test utils (<TextContext>, renderWithRedux, renderHook) out of ra-core into a new ra-test package. that's technically a breaking change, but not on production code, and the upgrade path takes 1 line:

-import { renderWithRedux, useMatchingReferences} from 'react-admin';
+import { useMatchingReferences} from 'react-admin';
+import { renderWithRedux } from 'ra-test';

Our simple example now uses Vite.js instead of Webpack, and this has boosted our productivity. the Simple example CodeSandbox still relies on Webpack, though.

A Word About React-Admin Enterprise Edition

If you like the new features we've added to react-admin, you're going to love what we've also published in react-admin Enterprise Edition. We'll publish a dedicated blog post about it, but here are the highlights:

  • ra-audit-log is a new module that allows to track the changes in every resource

ra-audit-log

  • The <Breadcrumb> component from ra-navigation got a major overhaul, allowing deeper customization
  • The ra-realtime package now allows customizing the side effects triggered when a real-time update occurs.
  • <TreeWithDetails>, from the ra-tree package, now allows to hide the root node

If you're interested in any of these new features, give React-admin Enterprise Edition a try!

Did you like this article? Share it!