added useEntity and useEntityQuery hooks

This commit is contained in:
dswbx
2024-12-12 17:00:10 +01:00
parent 9d9aa7b7a5
commit 50c5adce0c
13 changed files with 456 additions and 38 deletions

View File

@@ -61,7 +61,7 @@
"navigation": [
{
"group": "Getting Started",
"pages": ["introduction", "setup", "sdk", "cli"]
"pages": ["introduction", "setup", "sdk", "react", "cli"]
},
{
"group": "Modules",

194
docs/react.mdx Normal file
View File

@@ -0,0 +1,194 @@
---
title: 'SDK (React)'
description: 'Use the bknd SDK for React'
---
For all SDK options targeting React, you always have 2 options:
1. use simple hooks which are solely based on the [API](/sdk)
2. use the query hook that makes wraps the API in [SWR](https://swr.vercel.app/)
To use the simple hook that returns the Api, you can use:
```tsx
import { useApi } from "bknd/client";
export default function App() {
const api = useApi();
// ...
}
```
## `useApiQuery([selector], [options])`
This hook wraps the API class in an SWR hook for convenience. You can use any API endpoint
supported, like so:
```tsx
import { useApiQuery } from "bknd/client";
export default function App() {
const { data, ...swr } = useApiQuery((api) => api.data.readMany("comments"));
if (swr.error) return <div>Error</div>
if (swr.isLoading) return <div>Loading...</div>
return <pre>{JSON.stringify(data, null, 2)}</pre>
}
```
### Props
* `selector: (api: Api) => FetchPromise`
The first parameter is a selector function that provides an Api instance and expects an
endpoint function to be returned.
* `options`: optional object that inherits from `SWRConfiguration`
```ts
type Options<Data> = SWRConfiguration & {
enabled?: boolean;
refine?: (data: Data) => Data | any;
}
```
* `enabled`: Determines whether this hook should trigger a fetch of the data or not.
* `refine`: Optional refinement that is called after a response from the API has been
received. Useful to omit irrelevant data from the response (see example below).
### Using mutations
To query and mutate data using this hook, you can leverage the parameters returned. In the
following example we'll also use a `refine` function as well as `revalidateOnFocus` (option from
`SWRConfiguration`) so that our data keeps updating on window focus change.
```tsx
import { useState } from "react";
import { useApiQuery } from "bknd/client";
export default function App() {
const [text, setText] = useState("");
const { data, api, mutate, ...q } = useApiQuery(
(api) => api.data.readOne("comments", 1),
{
// filter to a subset of the response
refine: (data) => data.data,
revalidateOnFocus: true
}
);
const comment = data ? data : null;
useEffect(() => {
setText(comment?.content ?? "");
}, [comment]);
if (q.error) return <div>Error</div>
if (q.isLoading) return <div>Loading...</div>
return (
<form
onSubmit={async (e) => {
e.preventDefault();
if (!comment) return;
// this will automatically revalidate the query
await mutate(async () => {
const res = await api.data.updateOne("comments", comment.id, {
content: text
});
return res.data;
});
return false;
}}
>
<input type="text" value={text} onChange={(e) => setText(e.target.value)} />
<button type="submit">Update</button>
</form>
);
}
```
## `useEntity()`
This hook wraps the endpoints of `DataApi` and returns CRUD options as parameters:
```tsx
import { useState } from "react",
import { useEntity } from "bknd/client";
export default function App() {
const [data, setData] = useState<any>();
const { create, read, update, _delete } = useEntity("comments", 1);
useEffect(() => {
read().then(setData);
}, []);
return <pre>{JSON.stringify(data, null, 2)}</pre>
}
```
If you only supply the entity name as string without an ID, the `read` method will fetch a list
of entities instead of a single entry.
### Props
Following props are available when using `useEntityQuery([entity], [id?])`:
- `entity: string`: Specify the table name of the entity
- `id?: number | string`: If an id given, it will fetch a single entry, otherwise a list
### Returned actions
The following actions are returned from this hook:
- `create: (input: object)`: Create a new entry
- `read: (query: Partial<RepoQuery> = {})`: If an id was given,
it returns a single item, otherwise a list
- `update: (input: object, id?: number | string)`: If an id was given, the id parameter is
optional. Updates the given entry partially.
- `_delete: (id?: number | string)`: If an id was given, the id parameter is
optional. Deletes the given entry.
## `useEntityQuery()`
This hook wraps the actions from `useEntity` around `SWR`. The previous example would look like
this:
```tsx
import { useState } from "react",
import { useEntityQuery } from "bknd/client";
export default function App() {
const { data } = useEntityQuery("comments", 1);
return <pre>{JSON.stringify(data, null, 2)}</pre>
}
```
### Using mutations
All actions returned from `useEntityQuery` are conveniently wrapped around the `mutate` function,
so you don't have think about this:
```tsx
import { useState } from "react";
import { useEntityQuery } from "bknd/client";
export default function App() {
const [text, setText] = useState("");
const { data, update, ...q } = useEntityQuery("comments", 1);
const comment = data ? data : null;
useEffect(() => {
setText(comment?.content ?? "");
}, [comment]);
if (q.error) return <div>Error</div>
if (q.isLoading) return <div>Loading...</div>
return (
<form
onSubmit={async (e) => {
e.preventDefault();
if (!comment) return;
// this will automatically revalidate the query
await update({ content: text });
return false;
}}
>
<input type="text" value={text} onChange={(e) => setText(e.target.value)} />
<button type="submit">Update</button>
</form>
);
}
```