React-admin 2.1 Is Out

François Zaninotto
François ZaninottoJuly 02, 2018
#admin-on-rest#react-admin#react#oss

After three bug fix releases since the react-admin 2.0 publication, it's time for our monthly batch of new features. We've listened to the feedback of our users to add much-wanted features. React-admin 2.1 was just published, and here is a highlight of what's new.

Tab Routes

The <TabbedForm> and <TabbedShowLayout> leverage material-ui's <Tab> component to decompose complex data sets into tabs. But until today, when users navigated between tabs, the URL wouldn't change accordingly. That means that navigation with the Back button was broken. React-admin 2.1 fixes that problem, and allows to create a link to any of the tabs.

Tab routes

The URL uses the tab index by default (0, 1, 2, etc.), but you can customize the URL of a particular tab with the path prop:

const UserEdit = (props) => (
    <Edit {...props}>
        <TabbedForm>
            <FormTab label="Summary">
                <DisabledInput source="id" />
                <TextInput source="name" />
            </FormTab>
            <FormTab label="Security" path="security">
                <TextInput source="role" />
            </FormTab>
        </TabbedForm>
    </Edit>
);

Sort By Reference Name or Title

In a <Datagrid>, when using a <ReferenceField>, clicking on the column header used to return unexpected results. For instance, in a list of posts displaying the author name, clicking on the Author column header returns posts Not sorted by author name. This is because, by default, clicking on a column header requires a sort by the source prop, which is the related resource id. And identifiers are seldom a good sorting criteria.

React-admin 2.1 now allows you to specify the name of the field to use for sorting in a <ReferenceField>, using the sortBy prop:

<ReferenceField label="User" source="userId" reference="users" sortBy="user.name">
    <TextField source="name" />
</ReferenceField>

Whether or not your backend can handle composed sort fields is your responsibility, so you may have to tweak your API to allow for this feature. But once the backend supports it, it should work without any change to the dataProvider code.

As a matter of fact, the sortBy prop is supported in all Field components. This is particularly useful for the <FunctionField>, which can have no source:

<FunctionField
    label="Author"
    sortBy="last_name"
    render={record => `${record.author.first_name} ${record.author.last_name}`}
/>

Redirect Function

Did you ever need to redirect users after a form submission, but felt too constrained by the limited values accepted by the redirect prop? Until today, developers could only redirect to edit, show, list, and to a custom (fixed) URL. React-admin 2.1 adds support for a redirect function, called with the current basePath, the id of the created / edited resource, and the full record data as parameters.

This allows you to redirect to a related record, as in this Post Edit form:

// redirect to the related Author show page
const redirect = (basePath, id, data) => `/author/${data.author_id}/show`;
  
export const PostEdit = (props) => {
    <Edit {...props}>
        <SimpleForm redirect={redirect}>
            ...
        </SimpleForm>
    </Edit>
);

The <SaveButton>, <SimpleForm>, and <TabbedForm> components all accept a function as redirect prop.

Easier Layout Customization

Most React-admin implementations need a custom Toolbar, or a custom Menu. You can already provide a custom appLayout to the <Admin> component to do so. But writing a custom layout used to imply copying the default react-admin <Layout>, and tweaking it. It always feel uncomfortable to copy a bunch of code just to change a line or two. So we've added the ability to create a custom layout just by passing custom menu, appbar, or notification components to the default <Layout>.

// in src/MyLayout.js
import { Layout } from 'react-admin';
import MyAppBar from './MyAppBar';
import MyMenu from './MyMenu';
import MyNotification from './MyNotification';

const MyLayout = (props) => <Layout 
    {...props}
    appBar={MyAppBar}
    menu={MyMenu}
    notification={MyNotification}
/>;

export default MyLayout;

The number of cases where you need a custom layout written from scratch should reduce drastically thanks to this change.

Tip: Now that you can customize the menu via the <Layout>, we're deprecating the ability to customize the menu directly from the <Admin> component. It's still possible, but it won't work anymore in the next major version.

Use <SimpleForm> and <TabbedForm> In Your Own Views

What if you need to display two forms on the same screen for two different resources? For instance, what if you need to display a media creation form alongside a post edition form? Or what if, when selecting a tag for a post, you realize that the one you need doesn't exist yet, and you want to create it without leaving the current view? These use cases are pretty common.

React-admin used to provide no particular component for that purpose. It wasn't impossible to implement, but it wasn't straightforward either. You could use redux-form and grab the data from the store using redux' connect(), but that required a good understanding of the inner workings of react-admin.

React-admin 2.1 makes it easier, by allowing to reuse the form components (<SimpleForm> and <TabbedForm>) outside of the <Create> and <Edit> views, and to set a custom form name. For instance, here is a custom Post creation view, using the dataProvider directly, and used inside a popup from the Comment edition view. Notice how the <SimpleForm> component sets a custom form name, distinct from the default one:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';

import {
    CREATE,
    LongTextInput,
    SimpleForm,
    TextInput,
    required,
    fetchEnd,
    fetchStart,
} from 'react-admin'; // eslint-disable-line import/no-unresolved
import dataProvider from '../dataProvider';

class PostQuickCreateView extends Component {
    handleSave = values => {
        const { fetchStart, fetchEnd, onSave } = this.props;

        fetchStart();
        dataProvider(CREATE, 'posts', { data: values })
            .then(({ data }) => {
                onSave(data);
            })
            .catch(error => {
                this.setState({ error });
            })
            .finally(() => {
                fetchEnd();
            });
    };

    render() {
        const { submitting, onCancel } = this.props;

        return (
            <SimpleForm
                form="post-create"
                save={this.handleSave}
                saving={submitting}
                redirect={false}
            >
                <TextInput source="title" validate={required()} />
                <LongTextInput source="teaser" validate={required()} />
            </SimpleForm>
        );
    }
}

const mapStateToProps = state => ({
    submitting: state.admin.loading > 0,
});
const mapDispatchToProps = {
    fetchStart,
    fetchEnd,
};

export default connect(mapStateToProps, mapDispatchToProps)(
    PostQuickCreateView
);

Tip: This example uses the new fetchStart() and fetchEnd() action creators, which respectively increment and decrement the number of ongoing HTTP requests, and control the display of the main loading spinner.

To see a more complete example, head to the pull request #1955 on GitHub.

<RichTextInput> Now Supports Validation

The title says it all. By popular demand, all validator functions now work on the <RichTextInput> field. Validation errors are displayed under the editor in case of validation failure.

import {
    Create,
    SimpleForm,
    TextInput,
    required,
    minLength,
    maxLength,
} from 'react-admin';
import RichTextInput from 'ra-input-rich-text';
const validateTitle = [required()];
const validateBody = [required(), minLength(50), maxLength(255)];

export const PostCreate = (props) => (
    <Create {...props}>
        <SimpleForm>
            <TextInput source="title" validate={validateTitle} />
            <RichTextInput source="body" validate={validateBody} />
        </SimpleForm>
    </Create>
);

Conclusion

There are a few more minor additions to this version, check the complete 2.1.0 changelog for details. As always, if you find a bug in react-admin 2.1, please open an issue in the react-admin GitHub tracker.

We're getting more and more feedback from advanced usage of react-admin, which means that we have nailed the basic use cases. Many developers ask for more features, but we really want to keep the react-admin core small (and manageable). So we focus on improving the developer experience, and on never blocking developers because of an architectural choice. In other terms, we're working hard to make react-admin intuitive and flexible.

So far, we've managed to implement some impressive features without changing the react-admin core (see #1923 for an example). That confirms that we're on the right path.

We'll soon publish a series of tutorials for advanced react-admin usage. Stay tuned!