Skip to content

Commit 94952d9

Browse files
committed
internal: Add copilot instructions
1 parent 6ee0bce commit 94952d9

File tree

7 files changed

+496
-3
lines changed

7 files changed

+496
-3
lines changed

.github/copilot-instructions.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- Use [@data-client/rest guide](.github/instructions/rest.instructions.md) for REST API definitions.
2+
- Use [@data-client/react guide](.github/instructions/react.instructions.md) for React.
3+
- Use [@data-client/test guide](.github/instructions/test.instructions.md) when writing tests.
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
---
2+
applyTo: '**/*.tsx'
3+
---
4+
## Rendering
5+
6+
```ts
7+
// GET https://jsonplaceholder.typicode.com/todos/5
8+
const todo = useSuspense(TodoResource.get, { id: 5 });
9+
// GET https://jsonplaceholder.typicode.com/todos
10+
const todoList = useSuspense(TodoResource.getList);
11+
// GET https://jsonplaceholder.typicode.com/todos?userId=1
12+
const todoListByUser = useSuspense(TodoResource.getList, { userId: 1 });
13+
// subscriptions
14+
const todo = useLive(TodoResource.get, { id: 5 });
15+
// without fetch
16+
const todo = useCache(TodoResource.get, { id: 5 });
17+
const todo = useQuery(Todo, { id: 5 });
18+
```
19+
20+
## Mutations
21+
22+
```ts
23+
const ctrl = useController();
24+
// PUT https://jsonplaceholder.typicode.com/todos/5
25+
const updateTodo = todo => ctrl.fetch(TodoResource.update, { id }, todo);
26+
// PATCH https://jsonplaceholder.typicode.com/todos/5
27+
const partialUpdateTodo = todo =>
28+
ctrl.fetch(TodoResource.partialUpdate, { id }, todo);
29+
// POST https://jsonplaceholder.typicode.com/todos
30+
const addTodoToBeginning = todo =>
31+
ctrl.fetch(TodoResource.getList.unshift, todo);
32+
// POST https://jsonplaceholder.typicode.com/todos?userId=1
33+
const addTodoToEnd = todo => ctrl.fetch(TodoResource.getList.push, { userId: 1 }, todo);
34+
// DELETE https://jsonplaceholder.typicode.com/todos/5
35+
const deleteTodo = id => ctrl.fetch(TodoResource.delete, { id });
36+
// GET https://jsonplaceholder.typicode.com/todos?userId=1&page=2
37+
const getNextPage = (page) => ctrl.fetch(TodoResource.getList.getPage, { userId: 1, page })
38+
```
39+
40+
## Helpful hooks
41+
42+
```tsx
43+
const [handleSubmit, loading, error] = useLoading(
44+
async data => {
45+
const post = await ctrl.fetch(PostResource.getList.push, data);
46+
navigateToPost(post.id);
47+
},
48+
[ctrl],
49+
);
50+
```
51+
52+
```tsx
53+
const [query, setQuery] = React.useState('');
54+
const handleChange = e => setQuery(e.currentTarget.value);
55+
const [debouncedQuery, isPending] = useDebounce(query, 200);
56+
57+
return (
58+
<AsyncBoundary fallback={<Loading />}>
59+
<IssueList query={debouncedQuery} owner="facebook" repo="react" />
60+
</AsyncBoundary>
61+
)
62+
```
63+
64+
## Components
65+
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.
67+
68+
```tsx
69+
<AsyncBoundary listen={history.listen}>
70+
<TodoList />
71+
</AsyncBoundary>
72+
```
73+
74+
## Type-safe imperative actions
75+
76+
`Controller` is returned from `useController()`. It has: ctrl.fetch(), ctrl.fetchIfStale(), ctrl.expireAll(), ctrl.invalidate(), ctrl.invalidateAll(), ctrl.setResponse(), ctrl.set().
77+
78+
## Programmatic queries
79+
80+
```ts
81+
const queryRemainingTodos = new schema.Query(
82+
TodoResource.getList.schema,
83+
entries => entries.filter(todo => !todo.completed).length,
84+
);
85+
86+
const allRemainingTodos = useQuery(queryRemainingTodos);
87+
const firstUserRemainingTodos = useQuery(queryRemainingTodos, { userId: 1 });
88+
```
89+
90+
```ts
91+
const groupTodoByUser = new schema.Query(
92+
TodoResource.getList.schema,
93+
todos => Object.groupBy(todos, todo => todo.userId),
94+
);
95+
const todosByUser = useQuery(groupTodoByUser);
96+
```
97+
98+
---
99+
100+
## Managers
101+
102+
Customer managers allow for global side effect handling. They interface with the store using `Controller`, and middleware is run in response to actions.
103+
104+
```ts
105+
import type { Manager, Middleware, EntityInterface } from '@data-client/react';
106+
import { actionTypes } from '@data-client/react';
107+
import isEntity from './isEntity';
108+
109+
export default class CustomSubsManager implements Manager {
110+
protected declare entities: Record<string, EntityInterface>;
111+
112+
middleware: Middleware = controller => next => async action => {
113+
switch (action.type) {
114+
case actionTypes.SUBSCRIBE:
115+
case actionTypes.UNSUBSCRIBE:
116+
const { schema } = action.endpoint;
117+
// only process registered entities
118+
if (schema && isEntity(schema) && schema.key in this.entities) {
119+
if (action.type === actionTypes.SUBSCRIBE) {
120+
this.subscribe(schema.key, action.args[0]?.product_id);
121+
} else {
122+
this.unsubscribe(schema.key, action.args[0]?.product_id);
123+
}
124+
125+
// consume subscription if we use it
126+
return Promise.resolve();
127+
}
128+
default:
129+
return next(action);
130+
}
131+
};
132+
133+
cleanup() {}
134+
135+
subscribe(channel: string, product_id: string) {}
136+
unsubscribe(channel: string, product_id: string) {}
137+
}
138+
```
139+
140+
## Best Practices & Notes
141+
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.
145+
146+
# Official Documentation Links
147+
148+
- [Rendering](https://dataclient.io/docs/getting-started/data-dependency)
149+
- [Mutations](https://dataclient.io/docs/getting-started/mutations)
150+
- [Managers](https://dataclient.io/docs/concepts/managers)
151+
- [useSuspense](https://dataclient.io/docs/api/useSuspense)
152+
- [Controller](https://dataclient.io/docs/api/Controller)
153+
154+
**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.**

0 commit comments

Comments
 (0)