Skip to content

Commit aecd59b

Browse files
authored
enhance: Improve renderDataCompose interface (#3591)
1 parent c87a638 commit aecd59b

File tree

9 files changed

+332
-294
lines changed

9 files changed

+332
-294
lines changed

.changeset/icy-hairs-go.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
'@data-client/vue': minor
3+
---
4+
5+
`renderDataCompose()` awaits until the composable runs
6+
7+
### Before
8+
9+
```ts
10+
const { result, cleanup } = renderDataCompose(() =>
11+
useCache(CoolerArticleResource.get, { id: payload.id }),
12+
);
13+
14+
// Wait for initial render
15+
await waitForNextUpdate();
16+
17+
expect(result.current).toBeDefined();
18+
```
19+
20+
### After
21+
22+
```ts
23+
const { result, cleanup } = await renderDataCompose(() =>
24+
useCache(CoolerArticleResource.get, { id: payload.id }),
25+
);
26+
27+
expect(result.value).toBeDefined();
28+
```

.changeset/kind-garlics-poke.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@data-client/vue': minor
3+
---
4+
5+
`renderDataCompose().result` is now simply passes the composable result if it's a ref, or wraps it as computable ref

packages/vue/src/__tests__/integration-garbage-collection.web.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('Integration Garbage Collection Web (Vue)', () => {
4545
content: 'Test Content',
4646
};
4747

48-
const { result, cleanup, waitForNextUpdate } = renderDataCompose(
48+
const { result, cleanup, waitForNextUpdate } = await renderDataCompose(
4949
() => useSuspense(ArticleResource.get, { id: 1 }),
5050
{
5151
initialFixtures: [
@@ -64,20 +64,20 @@ describe('Integration Garbage Collection Web (Vue)', () => {
6464

6565
await waitForNextUpdate();
6666

67-
const articleRef = await result.current;
67+
const articleRef = await result.value;
6868
expect(articleRef?.value.title).toBe(articleData.title);
6969
expect(articleRef?.value.content).toBe(articleData.content);
7070

7171
cleanup();
7272
});
7373

74-
it('should work with useQuery and GCPolicy', () => {
74+
it('should work with useQuery and GCPolicy', async () => {
7575
const articleListData = [
7676
{ id: 1, title: 'Article 1', content: 'Content 1' },
7777
{ id: 2, title: 'Article 2', content: 'Content 2' },
7878
];
7979

80-
const { result, cleanup } = renderDataCompose(
80+
const { result, cleanup } = await renderDataCompose(
8181
() => useQuery(ArticleResource.getList.schema),
8282
{
8383
initialFixtures: [
@@ -94,9 +94,9 @@ describe('Integration Garbage Collection Web (Vue)', () => {
9494
},
9595
);
9696

97-
expect(result.current?.value).toBeDefined();
98-
expect(result.current?.value?.length).toBe(2);
99-
expect(result.current?.value?.[0]).toBeInstanceOf(Article);
97+
expect(result.value).toBeDefined();
98+
expect(result.value?.length).toBe(2);
99+
expect(result.value?.[0]).toBeInstanceOf(Article);
100100

101101
cleanup();
102102
});

packages/vue/src/__tests__/useCache.web.ts

Lines changed: 29 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -60,24 +60,18 @@ describe('vue useCache()', () => {
6060
});
6161

6262
it('returns undefined on empty store (no fetch)', async () => {
63-
const { result, waitForNextUpdate, cleanup } = renderDataCompose(() =>
63+
const { result, cleanup } = await renderDataCompose(() =>
6464
useCache(CoolerArticleResource.get, { id: payload.id }),
6565
);
6666

67-
// Wait for initial render
68-
await waitForNextUpdate();
69-
70-
// Unlike useSuspense, useCache should return a ComputedRef immediately
71-
expect(result.current).toBeDefined();
72-
7367
// The value should be undefined since we haven't fetched yet
74-
expect(result.current!.value).toBeUndefined();
68+
expect(result.value).toBeUndefined();
7569

7670
cleanup();
7771
});
7872

7973
it('returns data when already in cache', async () => {
80-
const { result, waitForNextUpdate, cleanup } = renderDataCompose(
74+
const { result, waitForNextUpdate, cleanup } = await renderDataCompose(
8175
() => useCache(CoolerArticleResource.get, { id: payload.id }),
8276
{
8377
initialFixtures: [
@@ -93,19 +87,17 @@ describe('vue useCache()', () => {
9387
// Wait for initial render
9488
await waitForNextUpdate();
9589

96-
const articleRef = result.current!;
97-
9890
// The value should be the cached data
99-
expect(articleRef.value).toBeDefined();
100-
expect(articleRef.value!.title).toBe(payload.title);
101-
expect(articleRef.value!.content).toBe(payload.content);
91+
expect(result.value).toBeDefined();
92+
expect(result.value?.title).toBe(payload.title);
93+
expect(result.value?.content).toBe(payload.content);
10294

10395
cleanup();
10496
});
10597

10698
it('re-renders when controller.setResponse() updates data', async () => {
10799
const { result, controller, waitForNextUpdate, cleanup } =
108-
renderDataCompose(
100+
await renderDataCompose(
109101
() => useCache(CoolerArticleResource.get, { id: payload.id }),
110102
{
111103
initialFixtures: [
@@ -121,11 +113,9 @@ describe('vue useCache()', () => {
121113
// Wait for initial render
122114
await waitForNextUpdate();
123115

124-
const articleRef = result.current!;
125-
126116
// Verify initial values
127-
expect(articleRef.value!.title).toBe(payload.title);
128-
expect(articleRef.value!.content).toBe(payload.content);
117+
expect(result.value?.title).toBe(payload.title);
118+
expect(result.value?.content).toBe(payload.content);
129119

130120
// Update the store using controller.setResponse
131121
const newTitle = payload.title + ' updated';
@@ -140,15 +130,15 @@ describe('vue useCache()', () => {
140130
await nextTick();
141131

142132
// The ComputedRef should now have updated values (it's reactive!)
143-
expect(articleRef.value!.title).toBe(newTitle);
144-
expect(articleRef.value!.content).toBe(newContent);
133+
expect(result.value?.title).toBe(newTitle);
134+
expect(result.value?.content).toBe(newContent);
145135

146136
cleanup();
147137
});
148138

149139
it('re-renders when controller.fetch() mutates data', async () => {
150140
const { result, controller, waitForNextUpdate, cleanup } =
151-
renderDataCompose(
141+
await renderDataCompose(
152142
() => useCache(CoolerArticleResource.get, { id: payload.id }),
153143
{
154144
initialFixtures: [
@@ -164,11 +154,9 @@ describe('vue useCache()', () => {
164154
// Wait for initial render
165155
await waitForNextUpdate();
166156

167-
const articleRef = result.current!;
168-
169157
// Verify initial values
170-
expect(articleRef.value!.title).toBe(payload.title);
171-
expect(articleRef.value!.content).toBe(payload.content);
158+
expect(result.value?.title).toBe(payload.title);
159+
expect(result.value?.content).toBe(payload.content);
172160

173161
// Mutate the data using controller.fetch with update endpoint
174162
const updatedTitle = payload.title + ' mutated';
@@ -184,8 +172,8 @@ describe('vue useCache()', () => {
184172
await nextTick();
185173

186174
// The ComputedRef should now have updated values (it's reactive!)
187-
expect(articleRef.value!.title).toBe(updatedTitle);
188-
expect(articleRef.value!.content).toBe(updatedContent);
175+
expect(result.value?.title).toBe(updatedTitle);
176+
expect(result.value?.content).toBe(updatedContent);
189177

190178
cleanup();
191179
});
@@ -297,7 +285,7 @@ describe('vue useCache()', () => {
297285

298286
it('should handle null args by returning undefined', async () => {
299287
const props = reactive({ id: payload.id as number | null });
300-
const { result, waitForNextUpdate, cleanup } = renderDataCompose(
288+
const { result, waitForNextUpdate, cleanup } = await renderDataCompose(
301289
(props: { id: number | null }) =>
302290
useCache(
303291
CoolerArticleResource.get,
@@ -323,36 +311,34 @@ describe('vue useCache()', () => {
323311
// Wait for initial render
324312
await waitForNextUpdate();
325313

326-
const articleRef = result.current!;
327-
328-
expect(articleRef).toBeDefined();
314+
expect(result.value).toBeDefined();
329315

330316
// Verify initial values
331-
expect(articleRef.value?.title).toBe(payload.title);
332-
expect(articleRef.value?.content).toBe(payload.content);
317+
expect(result.value?.title).toBe(payload.title);
318+
expect(result.value?.content).toBe(payload.content);
333319

334320
// Change to null - the ComputedRef should reactively become undefined
335321
props.id = null;
336322
await nextTick();
337323

338324
// The same ComputedRef should now have undefined value
339-
expect(articleRef.value).toBeUndefined();
325+
expect(result.value).toBeUndefined();
340326

341327
// Change back to valid id - should get cached data
342328
props.id = payload2.id;
343329
await nextTick();
344330

345331
// The ComputedRef should now have the new article data
346-
expect(articleRef).toBeDefined();
347-
expect(articleRef?.value?.title).toBe(payload2.title);
348-
expect(articleRef.value?.content).toBe(payload2.content);
332+
expect(result.value).toBeDefined();
333+
expect(result.value?.title).toBe(payload2.title);
334+
expect(result.value?.content).toBe(payload2.content);
349335

350336
cleanup();
351337
});
352338

353339
it('returns undefined for stale data when invalidIfStale is true', async () => {
354340
const { result, controller, waitForNextUpdate, cleanup } =
355-
renderDataCompose(
341+
await renderDataCompose(
356342
() => useCache(CoolerArticleResource.get, { id: payload.id }),
357343
{
358344
initialFixtures: [
@@ -373,16 +359,14 @@ describe('vue useCache()', () => {
373359

374360
await nextTick();
375361

376-
const articleRef = result.current!;
377-
378362
// The value should be undefined since the data is invalid
379-
expect(articleRef.value).toBeUndefined();
363+
expect(result.value).toBeUndefined();
380364

381365
cleanup();
382366
});
383367

384368
it('returns cached data even if expired when expiryStatus is Valid', async () => {
385-
const { result, waitForNextUpdate, cleanup } = renderDataCompose(
369+
const { result, waitForNextUpdate, cleanup } = await renderDataCompose(
386370
() => useCache(CoolerArticleResource.get, { id: payload.id }),
387371
{
388372
initialFixtures: [
@@ -398,11 +382,9 @@ describe('vue useCache()', () => {
398382
// Wait for initial render
399383
await waitForNextUpdate();
400384

401-
const articleRef = result.current!;
402-
403385
// Even though data might be expired, if it's Valid it should be returned
404-
expect(articleRef.value).toBeDefined();
405-
expect(articleRef.value!.title).toBe(payload.title);
386+
expect(result.value).toBeDefined();
387+
expect(result.value?.title).toBe(payload.title);
406388

407389
cleanup();
408390
});

0 commit comments

Comments
 (0)