Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Doc] Update examples to access record and id from context instead of props #8335

Merged
merged 5 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 30 additions & 22 deletions docs/Actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,11 @@ The mutation hooks execute the query when you call a callback. They return an ar
For instance, here is an example using `useUpdate()`:

```jsx
import * as React from "react";
import { useUpdate, Button } from 'react-admin';
import * as React from 'react';
import { useUpdate, useRecordContext, Button } from 'react-admin';

const ApproveButton = ({ record }) => {
const ApproveButton = () => {
const record = useRecordContext();
const [approve, { isLoading }] = useUpdate('comments', { id: record.id, data: { isApproved: true }, previousData: record });
return <Button label="Approve" onClick={() => approve()} disabled={isLoading} />;
};
Expand Down Expand Up @@ -135,7 +136,7 @@ Both these hooks accept a query *key* (identifying the query in the cache), and
For instance, the initial code snippet of this chapter can be rewritten with `useQuery` as follows:

```jsx
import * as React from "react";
import * as React from 'react';
import { useQuery } from 'react-query';
import { useDataProvider, Loading, Error } from 'react-admin';

Expand All @@ -162,11 +163,12 @@ const UserProfile = ({ userId }) => {
To illustrate the usage of `useMutation`, here is an implementation of an "Approve" button for a comment:

```jsx
import * as React from "react";
import * as React from 'react';
import { useMutation } from 'react-query';
import { useDataProvider, Button } from 'react-admin';
import { useDataProvider, useRecordContext, Button } from 'react-admin';

const ApproveButton = ({ record }) => {
const ApproveButton = () => {
const record = useRecordContext();
const dataProvider = useDataProvider();
const { mutate, isLoading } = useMutation(
['comments', 'update', { id: record.id, data: { isApproved: true } }],
Expand Down Expand Up @@ -200,9 +202,10 @@ Let's see how what these variables contain in a typical usage scenario:
Components use the loading state to show a loading indicator when there is no data to show. In the example above, the loading indicator is necessary for step 2, but not in step 4, because you can display the stale data while fresh data is being loaded.

```jsx
import { useGetOne } from 'react-admin';
import { useGetOne, useRecordContext } from 'react-admin';

const UserProfile = ({ record }) => {
const UserProfile = () => {
const record = useRecordContext();
const { data, isLoading, error } = useGetOne('users', { id: record.id });
if (isLoading) { return <Loading />; }
if (error) { return <p>ERROR</p>; }
Expand Down Expand Up @@ -285,9 +288,10 @@ The data provider method hooks (like `useGetOne`) and react-query's hooks (like
For instance, if you want to execute a callback when the query completes (whether it's successful or failed), you can use the `onSettled` option. this can be useful e.g. to log all calls to the dataProvider:

```jsx
import { useGetOne } from 'react-admin';
import { useGetOne, useRecordContext } from 'react-admin';

const UserProfile = ({ record }) => {
const UserProfile = () => {
const record = useRecordContext();
const { data, isLoading, error } = useGetOne(
'users',
{ id: record.id },
Expand Down Expand Up @@ -350,10 +354,11 @@ To execute some logic after a query or a mutation is complete, use the `onSucces
This is very common when using mutation hooks like `useUpdate`, e.g. to display a notification, or redirect to another page. For instance, here is an `<ApproveButton>` that notifies the user of success or failure using the bottom notification banner:

```jsx
import * as React from "react";
import { useUpdate, useNotify, useRedirect, Button } from 'react-admin';
import * as React from 'react';
import { useUpdate, useNotify, useRedirect, useRecordContext, Button } from 'react-admin';

const ApproveButton = ({ record }) => {
const ApproveButton = () => {
const record = useRecordContext();
const notify = useNotify();
const redirect = useRedirect();
const [approve, { isLoading }] = useUpdate(
Expand Down Expand Up @@ -389,10 +394,11 @@ React-admin provides the following hooks to handle the most common side effects:
In the following example, after clicking on the "Approve" button, a loading spinner appears while the data provider is fetched. Then, users are redirected to the comments list.

```jsx
import * as React from "react";
import { useUpdate, useNotify, useRedirect, Button } from 'react-admin';
import * as React from 'react';
import { useUpdate, useNotify, useRedirect, useRecordContext, Button } from 'react-admin';

const ApproveButton = ({ record }) => {
const ApproveButton = () => {
const record = useRecordContext();
const notify = useNotify();
const redirect = useRedirect();
const [approve, { isLoading }] = useUpdate(
Expand Down Expand Up @@ -438,10 +444,11 @@ By default, react-admin uses the `undoable` mode for the Edit view. As for the d
You can benefit from optimistic and undoable modes when you call the `useUpdate` hook, too. You just need to pass a `mutationMode` option:

```diff
import * as React from "react";
import { useUpdate, useNotify, useRedirect, Button } from 'react-admin';
import * as React from 'react';
import { useUpdate, useNotify, useRedirect, useRecordContext, Button } from 'react-admin';

const ApproveButton = ({ record }) => {
const ApproveButton = () => {
const record = useRecordContext();
const notify = useNotify();
const redirect = useRedirect();
const [approve, { isLoading }] = useUpdate(
Expand Down Expand Up @@ -481,9 +488,10 @@ There is no special react-admin sauce in that case. Here is an example implement
// in src/comments/ApproveButton.js
import * as React from 'react';
import { useState } from 'react';
import { useNotify, useRedirect, fetchStart, fetchEnd, Button } from 'react-admin';
import { useNotify, useRedirect, useRecordContext, Button } from 'react-admin';

const ApproveButton = ({ record }) => {
const ApproveButton = () => {
const record = useRecordContext();
const redirect = useRedirect();
const notify = useNotify();
const [loading, setLoading] = useState(false);
Expand Down
19 changes: 12 additions & 7 deletions docs/ArrayField.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,16 @@ And here is how to display all the tags of the current post as `<Chip>` componen
**Tip**: If you need to render a custom collection, it's often simpler to write your own component:

```jsx
const TagsField = ({ record }) => (
<ul>
{record.tags.map(item => (
<li key={item.name}>{item.name}</li>
))}
</ul>
)
import { useRecordContext } from 'react-admin';

const TagsField = () => {
const record = useRecordContext();
return (
<ul>
{record.tags.map(item => (
<li key={item.name}>{item.name}</li>
))}
</ul>
)
};
```
61 changes: 34 additions & 27 deletions docs/Create.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ For instance, the following component will render a creation form with 4 inputs

```jsx
// in src/posts.js
import * as React from "react";
import * as React from 'react';
import { Create, SimpleForm, TextInput, DateInput, required } from 'react-admin';
import RichTextInput from 'ra-input-rich-text';

Expand All @@ -35,7 +35,7 @@ export const PostCreate = () => (
);

// in src/App.js
import * as React from "react";
import * as React from 'react';
import { Admin, Resource } from 'react-admin';
import jsonServerProvider from 'ra-data-json-server';

Expand Down Expand Up @@ -71,7 +71,7 @@ You can customize the `<Create>` component using the following props:
You can replace the list of default actions by your own elements using the `actions` prop:

```jsx
import * as React from "react";
import * as React from 'react';
import Button from '@mui/material/Button';
import { TopToolbar, Create } from 'react-admin';

Expand Down Expand Up @@ -433,21 +433,24 @@ That means that if you want to create a link to a creation form, presetting *som
{% raw %}
```jsx
import * as React from 'react';
import { Datagrid } from 'react-admin';
import { Datagrid, useRecordContext } from 'react-admin';
import { Button } from '@mui/material';
import { Link } from 'react-router-dom';

const CreateRelatedCommentButton = ({ record }) => (
<Button
component={Link}
to={{
pathname: '/comments/create',
}}
state={{ record: { post_id: record.id } }}
>
Write a comment for that post
</Button>
);
const CreateRelatedCommentButton = () => {
const record = useRecordContext();
return (
<Button
component={Link}
to={{
pathname: '/comments/create',
}}
state={{ record: { post_id: record.id } }}
>
Write a comment for that post
</Button>
);
};

export default PostList = () => (
<List>
Expand All @@ -466,21 +469,25 @@ export default PostList = () => (

{% raw %}
```jsx
import * as React from "react";
import * as React from 'react';
import { useRecordContext } from 'react-admin';
import Button from '@mui/material/Button';
import { Link } from 'react-router-dom';

const CreateRelatedCommentButton = ({ record }) => (
<Button
component={Link}
to={{
pathname: '/comments/create',
search: `?source=${JSON.stringify({ post_id: record.id })}`,
}}
>
Write a comment for that post
</Button>
);
const CreateRelatedCommentButton = () => {
const record = useRecordContext();
return (
<Button
component={Link}
to={{
pathname: '/comments/create',
search: `?source=${JSON.stringify({ post_id: record.id })}`,
}}
>
Write a comment for that post
</Button>
);
};
```
{% endraw %}

Expand Down
30 changes: 18 additions & 12 deletions docs/EditTutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -756,21 +756,27 @@ All react-admin inputs handle the display of their label by wrapping their conte
You can wrap your own components inside the `<Labeled>` component too. You can either provide it the `label` prop directly or leverage the automatic label inference by providing it the `source` prop:

```jsx
const IdentifierField = ({ label, record }) => (
<Labeled label={label}>
<Typography>{record.id}</Typography>
</Labeled>
);
const IdentifierField = ({ label }) => {
const record = useRecordContext();
return (
<Labeled label={label}>
<Typography>{record.id}</Typography>
</Labeled>
);
};

// Here Labeled will try to translate the label with the translation key `resources.posts.fields.body`
// and with an inferred default of `Body`
const BodyField = ({ record }) => (
<Labeled source="body">
<Typography>
{record.body}
</Typography>
</Labeled>
);
const BodyField = () => {
const record = useRecordContext();
return (
<Labeled source="body">
<Typography>
{record.body}
</Typography>
</Labeled>
)
};

const PostEdit = () => (
<Create>
Expand Down
4 changes: 3 additions & 1 deletion docs/FilteringTutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@ For instance, if you have a list of tags, you can display a button for each cate

{% raw %}
```jsx
import { useTranslate, useRecordContext } from 'react-admin';
import Button from '@mui/material/Button';
import { Link } from 'react-router-dom';

const LinkToRelatedProducts = ({ record }) => {
const LinkToRelatedProducts = () => {
const record = useRecordContext();
const translate = useTranslate();
return record ? (
<Button
Expand Down
2 changes: 1 addition & 1 deletion docs/List.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ const Dashboard = () => (
```
{% endraw %}

Please note that the selection state is not synced in the URL but in a global store using the resource as key. Thus all lists in the page using the same resource will share the same selection state. This is a design choice because if row selection is not tied to a resource, then when a user deletes a record it may remain selected without any ability to unselect it. If you want the selection state to be local, you will have to implement your own `useListController` hook and pass a custom key to the `useRecordSelection` hook. You will then need to implement your own `DeleteButton` and `BulkDeleteButton` to manually unselect rows when deleting records.
Please note that the selection state is not synced in the URL but in a global store using the resource as key. Thus, all lists in the page using the same resource will share the same selection state. This is a design choice because if row selection is not tied to a resource, then when a user deletes a record it may remain selected without any ability to unselect it. If you want the selection state to be local, you will have to implement your own `useListController` hook and pass a custom key to the `useRecordSelection` hook. You will then need to implement your own `DeleteButton` and `BulkDeleteButton` to manually unselect rows when deleting records.

## `empty`: Empty Page Component

Expand Down
5 changes: 3 additions & 2 deletions docs/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ The `<Resource>` component is a configuration component that allows defining sub
```jsx
// in posts.js
import * as React from "react";
import { List, Datagrid, Edit, Create, SimpleForm, DateField, TextField, EditButton, TextInput, DateInput } from 'react-admin';
import { List, Datagrid, Edit, Create, SimpleForm, DateField, TextField, EditButton, TextInput, DateInput, useRecordContext } from 'react-admin';
import BookIcon from '@mui/icons-material/Book';
export const PostIcon = BookIcon;

Expand All @@ -74,7 +74,8 @@ export const PostList = () => (
</List>
);

const PostTitle = ({ record }) => {
const PostTitle = () => {
const record = useRecordContext();
return <span>Post {record ? `"${record.title}"` : ''}</span>;
};

Expand Down
5 changes: 3 additions & 2 deletions docs/Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ title: "Reference"
* [`useGetIdentity`](./useGetIdentity.md)
* [`useGetList`](./useGetList.md)
* [`useGetMany`](./useGetMany.md)
* [´useGetManyAggregate´](./useGetOne.md#aggregating-getone-calls)
* [`useGetManyReference`](./useGetManyReference.md)
* [`useGetOne`](./useGetOne.md)
* [`useGetPermissions`](./WithPermissions.md)
Expand All @@ -196,7 +197,7 @@ title: "Reference"
* [`useRecordContext`](./useRecordContext.md)
* `useRecordSelection`
* [`useRedirect`](./useRedirect.md)
* `useReference`
* [`useReference`](./useGetOne.md#aggregating-getone-calls)
* `useReferenceArrayFieldController`
* `useReferenceArrayInputController`
* `useReferenceInputController`
Expand All @@ -219,6 +220,6 @@ title: "Reference"
* [`useUpdateMany`](./useUpdateMany.md)
* [`useUnselect`](./useUnselect.md)
* [`useUnselectAll`](./useUnselectAll.md)
* `useWarnWhenUnsavedChanges`
* [`useWarnWhenUnsavedChanges`](./EditTutorial.md#warning-about-unsaved-changes)

</div>
1 change: 1 addition & 0 deletions docs/ShowTutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ const BookShow = () => {
The initial logic that grabs the id from the location and fetches the record from the API is also common, and react-admin exposes [the `useShowController` hook](./useShowController.md) to do it:

```diff
-import { useParams } from 'react-router-dom';
-import { useGetOne, useRedirect, RecordContextProvider, SimpleShowLayout, Title, TextField, DateField } from 'react-admin';
+import { useShowController, RecordContextProvider, SimpleShowLayout, Title, TextField, DateField } from 'react-admin';
import { Card } from '@mui/material';
Expand Down
28 changes: 28 additions & 0 deletions docs/Theming.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,34 @@ const MyAppBar = props => (
const MyLayout = props => <Layout {...props} appBar={MyAppBar} />;
```

## Changing the Theme Programmatically

React-admin provides the `useTheme` hook to read and update the theme programmatically. It uses the same syntax as `useState`.
Its used internally by `ToggleThemeButton` component.

```jsx
import { defaultTheme, useTheme } from 'react-admin';
import { Button } from '@mui/material';

const lightTheme = defaultTheme;
const darkTheme = {
...defaultTheme,
palette: {
mode: 'dark',
},
};

const ThemeToggler = () => {
const [theme, setTheme] = useTheme();

return (
<Button onClick={() => setTheme(theme.palette.mode === 'dark' ? lightTheme : darkTheme)}>
{theme.palette.mode === 'dark' ? 'Switch to light theme' : 'Switch to dark theme'}
</Button>
);
}
```

## Conditional Formatting

Sometimes you want the format to depend on the value. Use `useRecordContext` to grab the record in a component, and the `sx` prop to apply the format.
Expand Down
Loading