React Admin v3 Advanced Recipes: Creating a Record Related to the Current One

Gildas Garcia
Gildas GarciaSeptember 16, 2020
#react-admin#react#tutorial

This is an update of the first article in the series of advanced tutorials for React-admin, updated for v3. It assumes you already understand the basics of react-admin and have at least completed the official tutorial.

Update 10/24: An update to this article for react-admin v4 is available at React Admin v4 Advanced Recipes: Creating a Record Related to the Current One.

Today, we'll see how to add a button on a show or edit page to create a new resource related to the one displayed. For example, a new comment from the post show or edit pages. The create form should have its post already set when coming from the post page and it should redirect to the original post after creation.

The final result as a video

Initializing The Project

For the purpose of this tutorial, we'll use a very simple data model:

  • the post resource will only have a title, a teaser and a body
  • the comment resource will only have a creation date, a body and a reference to its post

We won't go into the details of all the components here as they are pretty straightforward but you can explore the code in this codesandbox.

Moreover, as we don't want to initialize a real API, we'll be using ra-data-fakerest as our dataProvider. It generates a fake API server from an object containing our data. In our case, this object looks like this:

const data = {
    posts: [
        {
            id: 1,
            title: "...",
            teaser: "...",
            body: "<p>...</p>"
        },
        ...
    ],
    comments: [
        {
            id: 1,
            post_id: 6,
            body: "...",
            created_at: new Date("2012-08-02")
        },
        ...
    ]
}

ra-data-fakerest will fake an API returning JSON data for routes such as:

  • GET /posts,
  • GET /posts/1
  • PUT /posts/1
  • POST /posts

Creating A New Comment From The Post Show Page

Back to our problem now. How can we set the post when creating a new comment?

React-admin automatically detects if a record was specified in the react-router location state. React-admin initialize the form values based on this location state.

So, we just need to create a link from our post page to the comment creation page, with the correct state.

Let's start with the easiest one, using the actions prop of the Show component:

// in src/posts/PostShow.js
// ...
import CardActions from "@material-ui/core/CardActions";
import ChatBubbleIcon from "@material-ui/icons/ChatBubble";
import { Button } from "react-admin";

const AddNewCommentButton = ({ record }) => (
  <Button
    component={Link}
    to={{
      pathname: "/comments/create",
      // Here we specify the initial record for the create view
      state: { record: { post_id: record.id } },
    }}
    label="Add a comment"
  >
    <ChatBubbleIcon />
  </Button>
);

const PostShowActions = ({ basePath, data }) => (
  <CardActions>
    <ListButton basePath={basePath} />
    <RefreshButton />
    <AddNewCommentButton record={data} />
  </CardActions>
);

const PostShow = props => (
  <Show {...props} actions={<PostShowActions />}>
    ...
  </Show>
);

We now have a button to create a new comment for the current post on our show page. Great!

Moving The Button Inside A Tab

However, this button is displayed on every tab. It would be better to show it only on the comments tab.

The show page with the create comment button inside its actions bar

We know from the documentation that every field component receives a record prop containing the current record (here, a post). Let's remove the actions prop and add the button below the comment datagrid:

// in src/posts/PostShow.js
// ...
const PostShow = props => (
  <Show {...props}>
    <TabbedShowLayout>
      ...
      <Tab label="Comments">
        <ReferenceManyField
          addLabel={false}
          reference="comments"
          target="post_id"
          sort={{ field: "created_at", order: "DESC" }}
        >
          <Datagrid>
            <DateField source="created_at" />
            <TextField source="body" />
            <EditButton />
          </Datagrid>
        </ReferenceManyField>
        <AddNewCommentButton />
      </Tab>
    </TabbedShowLayout>
  </Show>
);

Now our button is only displayed in the appropriate context, that's better for our users. We just need to style it a bit so that it's not so close to the Datagrid.

// in src/posts/PostShow.js
// ...
import ChatBubbleIcon from "@material-ui/icons/ChatBubble";
import { makeStyles } from "@material-ui/core/styles";
import { Button } from "react-admin";

const useStyles = makeStyles(theme => ({
  button: {
    marginTop: theme.spacing(1),
    marginBottom: theme.spacing(1)
  },
}));

const AddNewCommentButton = ({ record }) => {
  const classes = useStyles();

  return (
    <Button
      className={classes.button}
      variant="contained"
      component={Link}
      to={{
        pathname: "/comments/create",
        // Here we specify the initial record for the create view
        state: { record: { post_id: record.id } },
      }}
      label="Add a comment"
    >
      <ChatBubbleIcon />
    </Button>
  );
};

The show page with the create comment button below the comments table inside the comments tab

Hooray! We're done! Or are we? When submitting the new comment, we're redirected to its edit page. Wouldn't it be better to redirect our user to the comments tab of the post?

Redirecting To The Comments Tab Of The Post

The SimpleForm component accepts a redirect prop which can be edit, show, list, or our very own path:

// ...
import { useLocation } from 'react-router';

const CommentCreate = props => {
  // Read the post_id from the location
  const location = useLocation();
  const post_id =
    location.state && location.state.record
      ? location.state.record.post_id
      : undefined;

  const redirect = post_id ? `/posts/${post_id}/show/comments` : false;

  return (
    <Create {...props}>
      <SimpleForm
        initialValues={{ created_at: today }}
        redirect={redirect}
      >
        // ...
      </SimpleForm>
    </Create>
  );
};

And voila!

The final result as a video

Conclusion

Hopefully this article proved that not only does react-admin speeds up admin interfaces development, it also offers all that's necessary to customize the resulting UI. Morever, It's just Reactâ„¢, It's just JavaScriptâ„¢ !

You can explore the full code for this article in this codesandbox

Did you like this article? Share it!