Skip to content

Commit 78725c9

Browse files
committed
update data-fetching (wip)
1 parent 171b75d commit 78725c9

File tree

6 files changed

+202
-203
lines changed

6 files changed

+202
-203
lines changed

docs_headless/src/content/docs/data-fetching/Actions.md

Lines changed: 78 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
---
2-
layout: default
32
title: "Querying the API"
43
---
54

6-
# Querying the API
7-
85
React-admin provides special hooks to emit read and write queries to the [`dataProvider`](./DataProviders.md), which in turn sends requests to your API. Under the hood, it uses [React Query](https://tanstack.com/query/v5/) to call the `dataProvider` and cache the results.
96

107
<iframe src="https://www.youtube-nocookie.com/embed/c8tw2sUhKgc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="aspect-ratio: 16 / 9;width:100%;margin-bottom:1em;"></iframe>
@@ -19,7 +16,7 @@ For instance, here is how to query the Data Provider for a User record on mount,
1916

2017
```jsx
2118
import { useState, useEffect } from 'react';
22-
import { useDataProvider } from 'react-admin';
19+
import { useDataProvider } from 'ra-core';
2320
import { Loading, Error } from './MyComponents';
2421

2522
const UserProfile = ({ userId }) => {
@@ -75,7 +72,7 @@ const { isPending, error, data } = useGetOne(resource, { id });
7572
For instance, here is how to fetch one User record on mount using the `useGetOne` hook:
7673

7774
```jsx
78-
import { useGetOne } from 'react-admin';
75+
import { useGetOne } from 'ra-core';
7976
import { Loading, Error } from './MyComponents';
8077

8178
const UserProfile = ({ userId }) => {
@@ -152,25 +149,21 @@ In addition to the `useQuery` options, react-admin query hooks also accept callb
152149

153150
See the [Success and Error Side Effects](#success-and-error-side-effects) below for more details.
154151

155-
**Tip**: In react-admin components that use the query hooks, you can override the query options using the `queryOptions` prop. For instance, to log the dataProvider calls, in the `<List>` component, you can do the following:
152+
**Tip**: In react-admin components that use the query hooks, you can override the query options using the `queryOptions` prop. For instance, to log the dataProvider calls, in the `<ListBase>` component, you can do the following:
156153

157-
{% raw %}
158154
```jsx
159-
import { List, DataTable } from 'react-admin';
155+
import { ListBase } from 'ra-core';
160156

161157
const PostList = () => (
162-
<List
158+
<ListBase
163159
queryOptions={{ onSettled: (data, error) => console.log(data, error) }}
164160
>
165-
<DataTable>
166-
<DataTable.Col source="id" />
167-
<DataTable.Col source="title" />
168-
<DataTable.Col source="body" />
169-
</DataTable>
170-
</List>
161+
<div>
162+
{/* Custom list content */}
163+
</div>
164+
</ListBase>
171165
);
172166
```
173-
{% endraw %}
174167

175168
## Mutation Hooks
176169

@@ -195,7 +188,7 @@ For instance, here is a button that updates a comment record when clicked, using
195188

196189
```jsx
197190
import * as React from 'react';
198-
import { useUpdate, useRecordContext, Button } from 'react-admin';
191+
import { useUpdate, useRecordContext } from 'ra-core';
199192

200193
const ApproveButton = () => {
201194
const record = useRecordContext();
@@ -204,7 +197,7 @@ const ApproveButton = () => {
204197
data: { isApproved: true },
205198
previousData: record
206199
});
207-
return <Button label="Approve" onClick={() => approve()} disabled={isPending} />;
200+
return <button onClick={() => approve()} disabled={isPending}>Approve</button>;
208201
};
209202
```
210203

@@ -264,32 +257,33 @@ See [Optimistic Rendering and Undo](#optimistic-rendering-and-undo) below for mo
264257

265258
For instance, here is a button to approve the current comment that notifies the user of success or failure using the bottom notification banner:
266259

267-
{% raw %}
268260
```jsx
269261
import * as React from 'react';
270-
import { UpdateButton, useNotify, useRedirect } from 'react-admin';
262+
import { useUpdate, useNotify, useRedirect, useRecordContext } from 'ra-core';
271263

272264
const ApproveButton = () => {
265+
const record = useRecordContext();
273266
const notify = useNotify();
274267
const redirect = useRedirect();
275-
return <UpdateButton
276-
label="Approve"
277-
data={{ isApproved: true }}
278-
mutationOptions={{
279-
onSuccess: (data) => {
280-
// success side effects go here
281-
redirect('/comments');
282-
notify('Comment approved');
283-
},
284-
onError: (error) => {
285-
// failure side effects go here
286-
notify(`Comment approval error: ${error.message}`, { type: 'error' });
287-
},
288-
}}
289-
/>;
268+
const [approve, { isPending }] = useUpdate('comments', {
269+
id: record.id,
270+
data: { isApproved: true },
271+
previousData: record
272+
}, {
273+
onSuccess: (data) => {
274+
// success side effects go here
275+
redirect('/comments');
276+
notify('Comment approved');
277+
},
278+
onError: (error) => {
279+
// failure side effects go here
280+
notify(`Comment approval error: ${error.message}`, { type: 'error' });
281+
},
282+
});
283+
284+
return <button onClick={() => approve()} disabled={isPending}>Approve</button>;
290285
};
291286
```
292-
{% endraw %}
293287

294288
## `meta` Parameter
295289

@@ -316,11 +310,9 @@ To execute some logic after a query or a mutation is complete, use the `onSucces
316310

317311
**Tip**: React-admin provides the various hooks to handle the most common side effects:
318312

319-
- [`useNotify`](./useNotify.md): Return a function to display a notification.
320-
- [`useRedirect`](./useRedirect.md): Return a function to redirect the user to another page.
321-
- [`useRefresh`](./useRefresh.md): Return a function to force a rerender of the current view (equivalent to pressing the Refresh button).
322-
- [`useUnselect`](./useUnselect.md): Return a function to unselect lines in the current `<DataTable>` based on the ids passed to it.
323-
- [`useUnselectAll`](./useUnselectAll.md): Return a function to unselect all lines in the current `<DataTable>`.
313+
- [`useNotify`](../common/useNotify.md): Return a function to display a notification.
314+
- [`useRedirect`](../common/useRedirect.md): Return a function to redirect the user to another page.
315+
- [`useRefresh`](../common/useRefresh.md): Return a function to force a rerender of the current view (equivalent to pressing the Refresh button).
324316

325317
### `onSuccess`
326318

@@ -333,7 +325,7 @@ onSuccess(data, variables, context) { /* ... */ }
333325
This could be useful when you have different shapes for a resource in lists and single record views. In those cases, you might want to avoid react-admin to prefill the cache.
334326

335327
```tsx
336-
import { useGetList } from 'react-admin';
328+
import { useGetList } from 'ra-core';
337329
import { useQueryClient } from '@tanstack/react-query';
338330
import { ListView } from './ListView';
339331

@@ -366,8 +358,10 @@ onError(error, variables, context) { /* ... */ }
366358

367359
This is useful to notify users about the error for instance.
368360

369-
```tsx
370-
import { useGetOne, useNotify, useRecordContext } from 'react-admin';
361+
```jsx
362+
import { useGetOne, useNotify, useRecordContext } from 'ra-core';
363+
364+
const Loading = () => <div>Loading...</div>;
371365

372366
const UserProfile = () => {
373367
const record = useRecordContext();
@@ -394,7 +388,9 @@ onSettled(data, error, variables, context) { /* ... */ }
394388
This can be useful e.g. to log all calls to the dataProvider:
395389

396390
```jsx
397-
import { useGetOne, useRecordContext } from 'react-admin';
391+
import { useGetOne, useRecordContext } from 'ra-core';
392+
393+
const Loading = () => <div>Loading...</div>;
398394

399395
const UserProfile = () => {
400396
const record = useRecordContext();
@@ -435,7 +431,10 @@ For instance, the initial code snippet of this chapter can be rewritten with `us
435431
```jsx
436432
import * as React from 'react';
437433
import { useQuery } from '@tanstack/react-query';
438-
import { useDataProvider, Loading, Error } from 'react-admin';
434+
import { useDataProvider } from 'ra-core';
435+
436+
const Loading = () => <div>Loading...</div>;
437+
const Error = () => <div>Error occurred</div>;
439438

440439
const UserProfile = ({ userId }) => {
441440
const dataProvider = useDataProvider();
@@ -464,15 +463,17 @@ To illustrate the usage of `useMutation`, here is an implementation of an "Appro
464463
```jsx
465464
import * as React from 'react';
466465
import { useMutation } from '@tanstack/react-query';
467-
import { useDataProvider, useRecordContext, Button } from 'react-admin';
466+
import { useDataProvider, useRecordContext } from 'ra-core';
468467

469468
const ApproveButton = () => {
470469
const record = useRecordContext();
471470
const dataProvider = useDataProvider();
472471
const { mutate, isPending } = useMutation({
473472
mutationFn: () => dataProvider.update('comments', { id: record.id, data: { isApproved: true } })
474473
});
475-
return <Button label="Approve" onClick={() => mutate()} disabled={isPending} />;
474+
return <button onClick={() => mutate()} disabled={isPending}>
475+
{isPending ? 'Approving...' : 'Approve'}
476+
</button>;
476477
};
477478
```
478479

@@ -505,7 +506,9 @@ Let's see how what these variables contain in a typical usage scenario:
505506
Components use the pending 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.
506507

507508
```jsx
508-
import { useGetOne, useRecordContext } from 'react-admin';
509+
import { useGetOne, useRecordContext } from 'ra-core';
510+
511+
const Loading = () => <div>Loading...</div>;
509512

510513
const UserProfile = () => {
511514
const record = useRecordContext();
@@ -552,7 +555,9 @@ const BanUserButton = ({ userId }) => {
552555
const { mutate, isPending } = useMutation({
553556
mutationFn: () => dataProvider.banUser(userId)
554557
});
555-
return <Button label="Ban" onClick={() => mutate()} disabled={isPending} />;
558+
return <button onClick={() => mutate()} disabled={isPending}>
559+
{isPending ? 'Banning...' : 'Ban'}
560+
</button>;
556561
};
557562
```
558563

@@ -585,7 +590,7 @@ In the following example, after clicking on the "Approve" button, a loading spin
585590

586591
```jsx
587592
import * as React from 'react';
588-
import { useUpdate, useNotify, useRedirect, useRecordContext, Button } from 'react-admin';
593+
import { useUpdate, useNotify, useRedirect, useRecordContext } from 'ra-core';
589594

590595
const ApproveButton = () => {
591596
const record = useRecordContext();
@@ -605,7 +610,9 @@ const ApproveButton = () => {
605610
}
606611
);
607612

608-
return <Button label="Approve" onClick={() => approve()} disabled={isPending} />;
613+
return <button onClick={() => approve()} disabled={isPending}>
614+
{isPending ? 'Approving...' : 'Approve'}
615+
</button>;
609616
};
610617
```
611618

@@ -635,7 +642,7 @@ You can benefit from optimistic and undoable modes when you call the `useUpdate`
635642

636643
```diff
637644
import * as React from 'react';
638-
import { useUpdate, useNotify, useRedirect, useRecordContext, Button } from 'react-admin';
645+
import { useUpdate, useNotify, useRedirect, useRecordContext } from 'ra-core';
639646

640647
const ApproveButton = () => {
641648
const record = useRecordContext();
@@ -655,7 +662,9 @@ const ApproveButton = () => {
655662
onError: (error) => notify(`Error: ${error.message}`, { type: 'error' }),
656663
}
657664
);
658-
return <Button label="Approve" onClick={() => approve()} disabled={isPending} />;
665+
return <button onClick={() => approve()} disabled={isPending}>
666+
{isPending ? 'Approving...' : 'Approve'}
667+
</button>;
659668
};
660669
```
661670

@@ -677,20 +686,17 @@ If you need to refresh part of the UI after a user action, you can use TanStack
677686
For example, the following button deletes an order and refreshes the list of orders so that the deleted order disappears:
678687

679688
```jsx
680-
import { useDataProvider, useNotify } from "react-admin";
689+
import { useDataProvider, useNotify } from "ra-core";
681690
import { useMutation, useQueryClient } from "@tanstack/react-query";
682-
import { IconButton, Tooltip } from "@mui/material";
683-
import CancelIcon from "@mui/icons-material/Cancel";
684-
import type { Order } from "data-generator-retail";
685691

686-
export const OrderCancelButton = ({ order }: Props) => {
692+
export const OrderCancelButton = ({ order }) => {
687693
const notify = useNotify();
688694
const queryClient = useQueryClient();
689695

690696
const dataProvider = useDataProvider();
691697

692698
const mutation = useMutation({
693-
mutationFn: (order: Order) =>
699+
mutationFn: (order) =>
694700
dataProvider.update("orders", {
695701
id: order.id,
696702
data: { status: "cancelled" },
@@ -705,21 +711,18 @@ export const OrderCancelButton = ({ order }: Props) => {
705711
},
706712
});
707713

708-
const handleCancel = (order: Order) => {
714+
const handleCancel = (order) => {
709715
mutation.mutate(order);
710716
};
711717

712718
return (
713-
<Tooltip title="Cancel order" placement="left">
714-
<IconButton
715-
color="error"
716-
aria-label="Cancel order"
717-
onClick={() => handleCancel(order)}
718-
disabled={mutation.isPending}
719-
>
720-
<CancelIcon />
721-
</IconButton>
722-
</Tooltip>
719+
<button
720+
title="Cancel order"
721+
onClick={() => handleCancel(order)}
722+
disabled={mutation.isPending}
723+
>
724+
{mutation.isPending ? 'Cancelling...' : 'Cancel order'}
725+
</button>
723726
);
724727
};
725728
```
@@ -736,7 +739,7 @@ There is no special react-admin sauce in that case. Here is an example implement
736739
// in src/comments/ApproveButton.js
737740
import * as React from 'react';
738741
import { useState } from 'react';
739-
import { useNotify, useRedirect, useRecordContext, Button } from 'react-admin';
742+
import { useNotify, useRedirect, useRecordContext } from 'ra-core';
740743

741744
const ApproveButton = () => {
742745
const record = useRecordContext();
@@ -758,7 +761,9 @@ const ApproveButton = () => {
758761
setLoading(false);
759762
});
760763
};
761-
return <Button label="Approve" onClick={handleClick} disabled={loading} />;
764+
return <button onClick={handleClick} disabled={loading}>
765+
{loading ? 'Approving...' : 'Approve'}
766+
</button>;
762767
};
763768

764769
export default ApproveButton;

docs_headless/src/content/docs/data-fetching/DataProviderList.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
---
2-
layout: default
32
title: "Supported Data Provider Backends"
43
---
54

6-
# Supported Data Provider Backends
5+
Thanks to the Data Provider architecture, ra-core supports a lot of API backends. Check the list below for open-source packages developed and maintained by the core team and developers from the ra-core community.
76

8-
Thanks to the Data Provider architecture, react-admin supports a lot of API backends. Check the list below for open-source packages developed and maintained by the core team and developers from the react-admin community.
9-
10-
If you can't find a Data Provider for your backend below, no worries! [Writing a Data Provider](./DataProviderWriting.md) takes a couple of hours, and won't prevent you from using react-admin.
7+
If you can't find a Data Provider for your backend below, no worries! [Writing a Data Provider](./DataProviderWriting.md) takes a couple of hours, and won't prevent you from using ra-core.
118

129
<div class="providers-list" markdown="1">
10+
1311
* ![Appwrite Logo](../img/backend-logos/appwrite.svg "Appwrite Logo")**[Appwrite](https://appwrite.io/)**: [marmelab/ra-appwrite](https://github.com/marmelab/ra-appwrite)
1412
* ![AWS Amplify Logo](../img/backend-logos/amplify.svg "AWS Amplify Logo")**[AWS Amplify](https://docs.amplify.aws)**: [MrHertal/react-admin-amplify](https://github.com/MrHertal/react-admin-amplify)
1513
* ![blitz Logo](../img/backend-logos/blitz.svg "blitz Logo")**[Blitz-js](https://blitzjs.com/docs)**: [theapexlab/ra-data-blitz](https://github.com/theapexlab/ra-data-blitz)
@@ -80,6 +78,7 @@ If you can't find a Data Provider for your backend below, no worries! [Writing a
8078
* ![surrealDB Logo](../img/backend-logos/surrealdb.svg "surrealDB Logo")**[SurrealDB](https://surrealdb.com/)**: [djedi23/ra-surrealdb](https://github.com/djedi23/ra-surrealdb)
8179
* ![treeql Logo](../img/backend-logos/treeql.png "treeql Logo")**[TreeQL / PHP-CRUD-API](https://treeql.org/)**: [nkappler/ra-data-treeql](https://github.com/nkappler/ra-data-treeql)
8280
* ![wooCommerce Logo](../img/backend-logos/wooCommerce.png "wooCommerce Logo")**[WooCommerce REST API](https://woocommerce.github.io/woocommerce-rest-api-docs)**: [zackha/ra-data-woocommerce](https://github.com/zackha/ra-data-woocommerce)
81+
8382
</div>
8483

8584
That's a long list!

0 commit comments

Comments
 (0)