@void-snippets/react 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +178 -0
- package/package.json +11 -2
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# @void-snippets/react
|
|
2
|
+
|
|
3
|
+
TanStack Query v5 hook factory for React. Generates a fully-typed set of hooks (`useList`, `useGet`, `useMutations`, `useInfinite`) from a `ResourceService` instance — with zero manual type annotations required.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @void-snippets/react @void-snippets/client @tanstack/react-query axios
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### 1. Configure axios once at your app entry point
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// src/main.ts
|
|
19
|
+
import axios from 'axios';
|
|
20
|
+
import { configure } from '@void-snippets/client';
|
|
21
|
+
|
|
22
|
+
const axiosInstance = axios.create({ baseURL: 'https://api.example.com' });
|
|
23
|
+
configure(axiosInstance);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 2. Set up TanStack Query
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// src/main.tsx
|
|
30
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
31
|
+
|
|
32
|
+
const queryClient = new QueryClient();
|
|
33
|
+
|
|
34
|
+
function App() {
|
|
35
|
+
return (
|
|
36
|
+
<QueryClientProvider client={queryClient}>
|
|
37
|
+
<YourApp />
|
|
38
|
+
</QueryClientProvider>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 3. Define a resource service
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
// contacts/contacts.api.ts
|
|
47
|
+
import { ResourceService } from '@void-snippets/client';
|
|
48
|
+
import type { Contact } from './contacts.types';
|
|
49
|
+
|
|
50
|
+
export class ContactsApiService extends ResourceService<
|
|
51
|
+
Contact.Id,
|
|
52
|
+
Contact.Base,
|
|
53
|
+
Contact.WithCreatedBy,
|
|
54
|
+
Contact.Apis.CreatePayload,
|
|
55
|
+
Contact.Apis.UpdatePayload
|
|
56
|
+
> {
|
|
57
|
+
constructor() {
|
|
58
|
+
super('/contacts');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const ContactsApis = new ContactsApiService();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 4. Create hooks
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
// contacts/contacts.hooks.ts
|
|
69
|
+
import { createResourceHooks } from '@void-snippets/react';
|
|
70
|
+
import { ContactsApis } from './contacts.api';
|
|
71
|
+
|
|
72
|
+
// All types are inferred from ContactsApis — no generics needed
|
|
73
|
+
export const contactHooks = createResourceHooks('contacts', ContactsApis);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 5. Use in components
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// List with pagination
|
|
80
|
+
function ContactsList() {
|
|
81
|
+
const { contacts, isContactsLoading, pagination } = contactHooks.useList({
|
|
82
|
+
page: 1,
|
|
83
|
+
limit: 20,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (isContactsLoading) return <Spinner />;
|
|
87
|
+
return contacts.map(c => <ContactCard key={c._id} contact={c} />);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Single item
|
|
91
|
+
function ContactDetail({ id }: { id: Contact.Id }) {
|
|
92
|
+
const { data, isLoading } = contactHooks.useGet(id);
|
|
93
|
+
// data is typed as Contact.WithCreatedBy ✅
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Mutations (auto-invalidate the list on success)
|
|
97
|
+
function CreateContactForm() {
|
|
98
|
+
const { create, update, delete: remove } = contactHooks.useMutations();
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<button onClick={() => create.mutate({ name: 'John' })}>
|
|
102
|
+
Create
|
|
103
|
+
</button>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Infinite scroll
|
|
108
|
+
function InfiniteContactsList() {
|
|
109
|
+
const { data, fetchNextPage, hasNextPage } = contactHooks.useInfinite({ limit: 20 });
|
|
110
|
+
const all = data?.pages.flatMap(p => p.items) ?? [];
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Custom API Response Shapes
|
|
117
|
+
|
|
118
|
+
By default the library expects this shape from your API:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
// List: { "data": { "items": [], "page": 1, "limit": 10, "totalPages": 5, "totalDocuments": 42 } }
|
|
122
|
+
// Single: { "data": { "_id": "...", "name": "..." } }
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
If your API looks different, pass adapters as a third argument:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
export const contactHooks = createResourceHooks('contacts', ContactsApis, {
|
|
129
|
+
adapters: {
|
|
130
|
+
fromList: (raw) => ({
|
|
131
|
+
items: raw.results,
|
|
132
|
+
pagination: {
|
|
133
|
+
page: raw.meta.page,
|
|
134
|
+
limit: raw.meta.perPage,
|
|
135
|
+
totalPages: raw.meta.lastPage,
|
|
136
|
+
totalDocuments: raw.meta.total,
|
|
137
|
+
},
|
|
138
|
+
}),
|
|
139
|
+
fromSingle: (raw) => raw.payload,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Hook Reference
|
|
147
|
+
|
|
148
|
+
### `useList(params?)`
|
|
149
|
+
| Returned key | Type | Description |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| `[prefix]` | `TBase[]` | e.g. `contacts` for prefix `"contacts"` |
|
|
152
|
+
| `pagination` | `TPagination` | `{ page, limit, totalPages, totalDocuments }` |
|
|
153
|
+
| `is[Prefix]Loading` | `boolean` | e.g. `isContactsLoading` |
|
|
154
|
+
| `[prefix]Error` | `Error \| null` | e.g. `contactsError` |
|
|
155
|
+
| `invalidate[Prefix]` | `() => void` | e.g. `invalidateContacts` |
|
|
156
|
+
|
|
157
|
+
### `useGet(id, staleTime?)`
|
|
158
|
+
Returns `{ data, isLoading, error, refetch }`. Skips the query if `id` is empty.
|
|
159
|
+
|
|
160
|
+
### `useMutations()`
|
|
161
|
+
Returns `{ create, update, delete }` — each is a TanStack `UseMutationResult`. All auto-invalidate the list cache on success.
|
|
162
|
+
|
|
163
|
+
### `useInfinite(params?)`
|
|
164
|
+
Returns the standard TanStack `useInfiniteQuery` result with `items` and `pagination` per page.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Part of the `@void-snippets` ecosystem
|
|
169
|
+
|
|
170
|
+
| Package | Description |
|
|
171
|
+
|---|---|
|
|
172
|
+
| `@void-snippets/core` | Shared types and utilities |
|
|
173
|
+
| `@void-snippets/client` | Framework-agnostic CRUD resource service (axios) |
|
|
174
|
+
| `@void-snippets/react` | This package |
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@void-snippets/react",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "TanStack Query resource hooks factory for React",
|
|
5
|
+
"homepage": "https://github.com/shahtirthhh/void-snippets",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/shahtirthhh/void-snippets.git",
|
|
9
|
+
"directory": "packages/react"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/shahtirthhh/void-snippets/issues"
|
|
13
|
+
},
|
|
5
14
|
"main": "./dist/index.js",
|
|
6
15
|
"module": "./dist/index.mjs",
|
|
7
16
|
"types": "./dist/index.d.ts",
|
|
@@ -30,7 +39,7 @@
|
|
|
30
39
|
},
|
|
31
40
|
"dependencies": {
|
|
32
41
|
"@tanstack/react-query": "^5.0.0",
|
|
33
|
-
"@void-snippets/core": "0.2.
|
|
42
|
+
"@void-snippets/core": "0.2.2"
|
|
34
43
|
},
|
|
35
44
|
"devDependencies": {
|
|
36
45
|
"@types/react": "^18.0.0",
|