Skip to content

Commit 3f89f18

Browse files
committed
internal: Move react-specific instructions to react instruction file and add more test instruction
1 parent b51b79e commit 3f89f18

File tree

3 files changed

+52
-55
lines changed

3 files changed

+52
-55
lines changed

.github/instructions/react.instructions.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
applyTo: '**/*.tsx'
33
---
4+
45
## Rendering
56

67
```ts
@@ -10,13 +11,15 @@ const todo = useSuspense(TodoResource.get, { id: 5 });
1011
const todoList = useSuspense(TodoResource.getList);
1112
// GET https://jsonplaceholder.typicode.com/todos?userId=1
1213
const todoListByUser = useSuspense(TodoResource.getList, { userId: 1 });
13-
// subscriptions
14+
// subscriptions with polling, websockets or SSE
1415
const todo = useLive(TodoResource.get, { id: 5 });
1516
// without fetch
1617
const todo = useCache(TodoResource.get, { id: 5 });
1718
const todo = useQuery(Todo, { id: 5 });
1819
```
1920

21+
For API definitions (like TodoResource), refer to the [@data-client/rest guide](.github/instructions/rest.instructions.md).
22+
2023
## Mutations
2124

2225
```ts
@@ -63,7 +66,8 @@ return (
6366

6467
## Components
6568

66-
Prefer using `AsyncBoundary` for error handling and loading states. Its props are `fallback`, `errorComponent`, and `errorClassName` and `listen`. It can be used to wrap any component that fetches data.
69+
Prefer using [AsyncBoundary](https://dataclient.io/docs/api/AsyncBoundary) for error handling and loading states.
70+
Its props are `fallback`, `errorComponent`, and `errorClassName` and `listen`. It can be used to wrap any component that fetches data.
6771

6872
```tsx
6973
<AsyncBoundary listen={history.listen}>
@@ -73,7 +77,8 @@ Prefer using `AsyncBoundary` for error handling and loading states. Its props ar
7377

7478
## Type-safe imperative actions
7579

76-
`Controller` is returned from `useController()`. It has: ctrl.fetch(), ctrl.fetchIfStale(), ctrl.expireAll(), ctrl.invalidate(), ctrl.invalidateAll(), ctrl.setResponse(), ctrl.set().
80+
[Controller](https://dataclient.io/docs/api/Controller) is returned from `useController()`. It has:
81+
ctrl.fetch(), ctrl.fetchIfStale(), ctrl.expireAll(), ctrl.invalidate(), ctrl.invalidateAll(), ctrl.setResponse(), ctrl.set().
7782

7883
## Programmatic queries
7984

@@ -99,7 +104,8 @@ const todosByUser = useQuery(groupTodoByUser);
99104

100105
## Managers
101106

102-
Customer managers allow for global side effect handling. They interface with the store using `Controller`, and middleware is run in response to actions.
107+
Customer [managers](https://dataclient.io/docs/api/Manager) allow for global side effect handling.
108+
They interface with the store using `Controller`, and middleware is run in response to actions.
103109

104110
```ts
105111
import type { Manager, Middleware, EntityInterface } from '@data-client/react';
@@ -139,9 +145,9 @@ export default class CustomSubsManager implements Manager {
139145

140146
## Best Practices & Notes
141147

142-
- `useDebounce(query, timeout);` when rendering async data based on user field inputs
143-
- `[handleSubmit, loading, error] = useLoading()` when tracking mutation loads like submitting an async form
144-
- Prefer smaller React components that do one thing.
148+
- [useDebounce(query, timeout)](https://dataclient.io/docs/api/useDebounce) when rendering async data based on user field inputs
149+
- [[handleSubmit, loading, error] = useLoading()](https://dataclient.io/docs/api/useLoading) when tracking async mutations
150+
- Prefer smaller React components that do one thing
145151

146152
# Official Documentation Links
147153

.github/instructions/rest.instructions.md

Lines changed: 29 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,26 @@ applyTo: '**/*.ts*'
33
---
44
# Guide: Using `@data-client/rest` for Resource Modeling
55

6-
This project uses `@data-client/rest` to define, fetch, normalize, and update RESTful resources and entities in React/TypeScript apps with type safety and automatic cache management.
6+
This project uses [@data-client/rest](https://dataclient.io/rest) to define, fetch, normalize, and update RESTful resources and entities in React/TypeScript apps with type safety and automatic cache management.
77
**Always follow these patterns when generating code that interacts with remote APIs.**
88

99
---
1010

1111
## 1. Defining Schemas
1212

13-
Define `schemas` to represent the JSON returned by an endpoint. Compose these
13+
Define [schemas](https://dataclient.io/rest/api/schema) to represent the JSON returned by an endpoint. Compose these
1414
to represent the data expected.
1515

1616
### Object
1717

18-
- `Entity` - represents a single unique object (denormalized)
19-
- `schema.Union(Entity)` - polymorphic objects (A | B)
18+
- [Entity](https://dataclient.io/rest/api/Entity) - represents a single unique object (denormalized)
19+
- [schema.Union(Entity)](https://dataclient.io/rest/api/Union) - polymorphic objects (A | B)
2020
- `{[key:string]: Schema}` - immutable objects
2121
- `schema.Invalidate(Entity)` - to delete an Entity
2222

2323
### List
2424

25-
- `schema.Collection([Entity])` - mutable/growable lists
25+
- [schema.Collection([Entity])](https://dataclient.io/rest/api/Collection) - mutable/growable lists
2626
- `[Entity]` - immutable lists
2727
- `schema.All(Entity)` - list all Entities of a kind
2828

@@ -33,7 +33,7 @@ to represent the data expected.
3333

3434
### Programmatic
3535

36-
- `schema.Query(Queryable)` - memoized programmatic selectors
36+
- [schema.Query(Queryable)](https://dataclient.io/rest/api/Query) - memoized programmatic selectors
3737

3838
---
3939

@@ -42,21 +42,19 @@ to represent the data expected.
4242
- Every `Entity` subclass **defines defaults** for _all_ non-optional serialised fields.
4343
- Override `pk()` only when the primary key ≠ `id`.
4444
- `pk()` return type is `number | string | undefined`
45-
- Always define nested Entities to be used in `static schema`
45+
- `static schema` (optional) for nested schemas or deserilization functions
4646
- When designing APIs, prefer nesting entities
47-
- use `static schema` to deserialize
48-
- must be a function (iso => new Date(iso))
4947
- always define `static key` as global registry for the Entity
50-
- Use `Entity.fromJS()` for defaults.
48+
- Use [Entity.fromJS()](https://dataclient.io/rest/api/Entity#fromJS) for defaults.
5149

5250
---
5351

5452
## 3. Resources (`resource()`)
5553

56-
- `resource()` creates a collection of `RestEndpoints` to perform CRUD operations on a common object
54+
- [resource()](https://dataclient.io/rest/api/resource) creates a collection of `RestEndpoints` for CRUD operations on a common object
5755
- Required fields:
5856
- `path`: path‑to‑regexp template (typed!)
59-
- `schema`: Declarative data shape for normalization (typically Entity or Union)
57+
- `schema`: Declarative data shape for a **single** item (typically Entity or Union)
6058
- Optional:
6159
- `urlPrefix`: Host root, if not `/`
6260
- `searchParams`: Type for query parameters (TS generic) in MyResource.getList
@@ -72,10 +70,12 @@ export class Todo extends Entity {
7270
user = User.fromJS();
7371
title = '';
7472
completed = false;
73+
createdAt = new Date();
7574

7675
static key = 'Todo';
7776
static schema = {
7877
user: User,
78+
createdAt: (iso: string) => new Date(iso),
7979
}
8080
}
8181

@@ -91,7 +91,7 @@ export const TodoResource = resource({
9191

9292
### Usage
9393

94-
#### Rendering
94+
#### [Rendering](https://dataclient.io/docs/getting-started/data-dependency)
9595

9696
```ts
9797
// GET https://jsonplaceholder.typicode.com/todos/5
@@ -100,14 +100,9 @@ const todo = useSuspense(TodoResource.get, { id: 5 });
100100
const todoList = useSuspense(TodoResource.getList);
101101
// GET https://jsonplaceholder.typicode.com/todos?userId=1
102102
const todoListByUser = useSuspense(TodoResource.getList, { userId: 1 });
103-
// subscriptions
104-
const todo = useLive(TodoResource.get, { id: 5 });
105-
// without fetch
106-
const todo = useCache(TodoResource.get, { id: 5 });
107-
const todo = useQuery(Todo, { id: 5 });
108103
```
109104

110-
#### Mutations
105+
#### [Mutations](https://dataclient.io/docs/getting-started/mutations)
111106

112107
```ts
113108
const ctrl = useController();
@@ -117,7 +112,7 @@ const updateTodo = todo => ctrl.fetch(TodoResource.update, { id }, todo);
117112
const partialUpdateTodo = todo =>
118113
ctrl.fetch(TodoResource.partialUpdate, { id }, todo);
119114
// POST https://jsonplaceholder.typicode.com/todos
120-
const addTodoToBeginning = todo =>
115+
const addTodoToStart = todo =>
121116
ctrl.fetch(TodoResource.getList.unshift, todo);
122117
// POST https://jsonplaceholder.typicode.com/todos?userId=1
123118
const addTodoToEnd = todo => ctrl.fetch(TodoResource.getList.push, { userId: 1 }, todo);
@@ -127,33 +122,27 @@ const deleteTodo = id => ctrl.fetch(TodoResource.delete, { id });
127122
const getNextPage = (page) => ctrl.fetch(TodoResource.getList.getPage, { userId: 1, page })
128123
```
129124

130-
#### Type-safe imperative actions
131-
132-
`Controller` is returned from `useController()`. It has: ctrl.fetch(), ctrl.fetchIfStale(), ctrl.expireAll(), ctrl.invalidate(), ctrl.invalidateAll(), ctrl.setResponse(), ctrl.set().
133-
134125
#### Programmatic queries
135126

136127
```ts
137128
const queryRemainingTodos = new schema.Query(
138129
TodoResource.getList.schema,
139130
entries => entries.filter(todo => !todo.completed).length,
140131
);
141-
142-
const allRemainingTodos = useQuery(queryRemainingTodos);
143-
const firstUserRemainingTodos = useQuery(queryRemainingTodos, { userId: 1 });
144132
```
145133

146134
```ts
147135
const groupTodoByUser = new schema.Query(
148136
TodoResource.getList.schema,
149137
todos => Object.groupBy(todos, todo => todo.userId),
150138
);
151-
const todosByUser = useQuery(groupTodoByUser);
152139
```
153140

141+
For more detailed usage, refer to the [@data-client/react guide](.github/instructions/react.instructions.md).
142+
154143
---
155144

156-
## 4. Custom RestEndpoint patterns
145+
## 4. Custom [RestEndpoint](https://dataclient.io/rest/api/RestEndpoint) patterns
157146

158147
```ts
159148
/** Stand‑alone endpoint with custom typing */
@@ -166,10 +155,10 @@ export const getTicker = new RestEndpoint({
166155
```
167156

168157
**Typing tips**
169-
- `path` params ▶️ 1st arg shape.
170-
- `method` ≠ `GET` ⇒ 2nd arg = body (unless `body: undefined`).
171-
- Provide `searchParams` / `body` _values_ purely for **type inference**.
172-
- Use `RestGenerics` when inheriting from `RestEndpoint`.
158+
- `path` path‑to‑regexp template for 1st arg
159+
- `method` ≠ `GET` ⇒ 2nd arg = body (unless `body: undefined`)
160+
- Provide `searchParams` / `body` _values_ purely for **type inference**
161+
- Use `RestGenerics` when inheriting from `RestEndpoint`
173162

174163
### getOptimisticResponse()
175164

@@ -188,7 +177,7 @@ getOptimisticResponse(snap, { id }) {
188177

189178
## 5. **Union Types (Polymorphic Schemas)**
190179

191-
To represent resources that can be multiple types (e.g., events), use `schema.Union` and a discriminator field.
180+
To define polymorphic resources (e.g., events), use [schema.Union](https://dataclient.io/rest/api/Union) and a discriminator field.
192181

193182
```typescript
194183
import { schema } from '@data-client/rest';
@@ -217,7 +206,7 @@ export const EventResource = resource({
217206

218207
## 7. **Extending Resources**
219208

220-
Use `.extend()` to add custom endpoints or behaviors.
209+
Use `.extend()` to add or override endpoints.
221210

222211
```typescript
223212
export const IssueResource = resource({
@@ -235,17 +224,14 @@ export const IssueResource = resource({
235224
## 8. Best Practices & Notes
236225

237226
- When asked to browse or navigate to a web address, actual visit the address
238-
- Always set up `schema` on every resource/entity/collection for normalization.
239-
- Prefer `RestEndpoint` over `resource` for defining single endpoints or when full CRUD endpoints don't exist
240-
- Normalize deeply nested or relational data by defining proper schemas.
241-
- `useDebounce(query, timeout);` when rendering async data based on user field inputs
242-
- `[handleSubmit, loading, error] = useLoading()` when tracking mutation loads like submitting an async form
243-
- Prefer smaller React components that do one thing.
227+
- Always set up `schema` on every resource/entity/collection for normalization
228+
- Prefer `RestEndpoint` over `resource()` for defining single endpoints or when mutation endpoints don't exist
229+
- Normalize deeply nested or relational data by defining proper schemas
244230

245231
## 9. Common Mistakes to Avoid
246232

247-
- Don’t forget to use `fromJS()` or assign default properties for class fields.
248-
- Don't use `resource` when mutation endpoints are not used or needed.
233+
- Don’t forget to use `fromJS()` or assign default properties for class fields
234+
- Don't use `resource()` when mutation endpoints are not used or needed
249235

250236
# Official Documentation Links
251237

.github/instructions/test.instructions.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ applyTo: '**/__tests__/*.ts*'
44

55
## Unit testing hooks
66

7+
- Use [renderDataHook()](https://dataclient.io/docs/api/renderDataHook) to test hooks that use [@data-client/react](https://dataclient.io/docs) hooks.
8+
- `renderDataHook()` inherits the options, and return values of `renderHook()` from [@testing-library/react](https://testing-library.com/docs/react-testing-library/api#renderhook).
9+
- Additional options include, `initialFixtures`, `resolverFixtures`, `getInitialInterceptorData`
10+
- Additional return values include `controller`, `cleanup()`, `allSettled()`
11+
712
```ts
813
import { renderDataHook } from '@data-client/test';
914

@@ -34,11 +39,11 @@ it('useSuspense() should render the response', async () => {
3439
});
3540
```
3641

37-
- use `initialFixtures` to set up the initial state of the store
38-
- use `resolverFixtures` to add interceptors to handle subsequent requests
42+
- use [initialFixtures](https://dataclient.io/docs/api/renderDataHook#optionsinitialfixtures) to set up the initial state of the store
43+
- use [resolverFixtures](https://dataclient.io/docs/api/renderDataHook#optionsresolverfixtures) to add interceptors to handle subsequent requests
3944
- use `getInitialInterceptorData` if `resolverFixtures` need to simulate changing server state
4045

41-
## Fixtures and Interceptors
46+
## [Fixtures and Interceptors](https://dataclient.io/docs/api/Fixtures)
4247

4348
```ts
4449
interface SuccessFixture {
@@ -77,12 +82,12 @@ type Interceptor = ResponseInterceptor | FetchInterceptor;
7782

7883
## Best Practices & Notes
7984

80-
- Use fixtures or interceptors when testing hooks or components.
85+
- Use [fixtures or interceptors](https://dataclient.io/docs/api/Fixtures) when testing hooks or components.
8186
- Use 'nock' when testing networking definitions.
8287

8388
# Official Documentation Links
8489

8590
- [Fixtures and Interceptors](https://dataclient.io/docs/api/Fixtures)
8691
- [renderDataHook()](https://dataclient.io/docs/api/renderDataHook)
8792

88-
**ALWAYS follow these patterns and refer to the official docs for edge cases. Prioritize code generation that is idiomatic, type-safe, and leverages automatic normalization/caching via schema definitions.**
93+
**ALWAYS follow these patterns and refer to the official docs for edge cases.**

0 commit comments

Comments
 (0)