Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plugins): add auto-refetch plugin #97

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions plugins/auto-refetch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<h1>
<img height="76" src="https://github.com/posva/pinia-colada/assets/664177/02011637-f94d-4a35-854a-02f7aed86a3c" alt="Pinia Colada logo">
Pinia Colada Auto Refetch
</h1>

<a href="https://npmjs.com/package/@pinia/colada-plugin-auto-refetch">
<img src="https://badgen.net/npm/v/@pinia/colada-plugin-auto-refetch/latest" alt="npm package">
</a>

Automatically refetch queries when they become stale in Pinia Colada.

## Installation

```sh
npm install @pinia/colada-plugin-auto-refetch
```

## Usage

```js
import { PiniaColadaAutoRefetch } from '@pinia/colada-plugin-auto-refetch'

// Pass the plugin to Pinia Colada options
app.use(PiniaColada, {
// ...
plugins: [
PiniaColadaAutoRefetch({ autoRefetch: true }), // enable globally
],
})
```

You can customize the refetch behavior individually for each query with the `autoRefetch` option:

```ts
useQuery({
key: ['todos'],
query: getTodos,
autoRefetch: true, // override local autoRefetch
})
```

## License

[MIT](http://opensource.org/licenses/MIT)
71 changes: 71 additions & 0 deletions plugins/auto-refetch/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"name": "@pinia/colada-plugin-auto-refetch",
"type": "module",
"publishConfig": {
"access": "public"
},
"version": "0.0.1",
"description": "Automatically refetch queries when they become stale in Pinia Colada",
"author": {
"name": "Yusuf Mansur Ozer",
"email": "[email protected]"
},
"license": "MIT",
"homepage": "https://github.com/posva/pinia-colada/plugins/auto-refetch#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/posva/pinia-colada.git"
},
"bugs": {
"url": "https://github.com/posva/pinia-colada/issues"
},
"keywords": [
"pinia",
"plugin",
"data",
"fetching",
"query",
"mutation",
"cache",
"layer",
"refetch"
],
"sideEffects": false,
"exports": {
".": {
"types": {
"import": "./dist/index.d.ts",
"require": "./dist/index.d.cts"
},
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"typesVersions": {
"*": {
"*": [
"./dist/*",
"./*"
]
}
},
"files": [
"LICENSE",
"README.md",
"dist"
],
"scripts": {
"build": "tsup",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s --commit-path . -l @pinia/colada-plugin-auto-refetch -r 1",
"test": "vitest --ui"
},
"peerDependencies": {
"@pinia/colada": "workspace:^"
},
"devDependencies": {
"@pinia/colada": "workspace:^"
}
}
160 changes: 160 additions & 0 deletions plugins/auto-refetch/src/auto-refetch.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/**
* @vitest-environment happy-dom
*/
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { defineComponent } from 'vue'
import { createPinia } from 'pinia'
import { useQuery, PiniaColada } from '@pinia/colada'
import type { UseQueryOptions } from '@pinia/colada'
import type { PiniaColadaAutoRefetchOptions } from '.'
import { PiniaColadaAutoRefetch } from '.'

describe('Auto Refetch plugin', () => {
beforeEach(() => {
vi.clearAllTimers()
vi.useFakeTimers()
})

afterEach(() => {
vi.restoreAllMocks()
})

enableAutoUnmount(afterEach)

function mountQuery(
queryOptions?: Partial<UseQueryOptions>,
pluginOptions?: PiniaColadaAutoRefetchOptions,
) {
const query = vi.fn(async () => 'result')
const wrapper = mount(
defineComponent({
template: '<div></div>',
setup() {
return useQuery({
query,
key: ['test'],
...queryOptions,
})
},
}),
{
global: {
plugins: [
createPinia(),
[PiniaColada, {
plugins: [PiniaColadaAutoRefetch({ autoRefetch: true, ...pluginOptions })],
...pluginOptions,
}],
],
},
},
)

return { wrapper, query }
}

it('automatically refetches when stale time is reached', async () => {
const { query } = mountQuery({
staleTime: 1000,
})

// Wait for initial query
await flushPromises()
expect(query).toHaveBeenCalledTimes(1)

// Advance time past stale time in one go
vi.advanceTimersByTime(1000)
await flushPromises()

expect(query).toHaveBeenCalledTimes(2)
})

it('respects enabled option globally', async () => {
const { query } = mountQuery(
{
staleTime: 1000,
},
{
autoRefetch: false,
},
)

await flushPromises()
expect(query).toHaveBeenCalledTimes(1)

vi.advanceTimersByTime(2000)
await flushPromises()
expect(query).toHaveBeenCalledTimes(1)
})

it('respects disabled option per query', async () => {
const { query } = mountQuery({
staleTime: 1000,
autoRefetch: false,
})

await flushPromises()
expect(query).toHaveBeenCalledTimes(1)

vi.advanceTimersByTime(2000)
await flushPromises()
expect(query).toHaveBeenCalledTimes(1)
})

it('avoids refetching an unactive query', async () => {
const { wrapper, query } = mountQuery({
staleTime: 1000,
})

await flushPromises()
expect(query).toHaveBeenCalledTimes(1)

wrapper.unmount()
vi.advanceTimersByTime(2000)
await flushPromises()
expect(query).toHaveBeenCalledTimes(1)
})

it('does not refetch when staleTime is not set', async () => {
const { query } = mountQuery({})

await flushPromises()
expect(query).toHaveBeenCalledTimes(1)

vi.advanceTimersByTime(2000)
await flushPromises()
expect(query).toHaveBeenCalledTimes(1)
})

it('resets the stale timer when a new request occurs', async () => {
const { query } = mountQuery({
staleTime: 1000,
})

// Wait for initial query
await flushPromises()
expect(query).toHaveBeenCalledTimes(1)

// Advance time partially (500ms)
vi.advanceTimersByTime(500)

// Manually trigger a new request
query.mockImplementationOnce(async () => 'new result')
await query()
await flushPromises()
expect(query).toHaveBeenCalledTimes(2)

// Advance time to what would have been the original stale time (500ms more)
vi.advanceTimersByTime(500)
await flushPromises()
// Should not have triggered another request yet
expect(query).toHaveBeenCalledTimes(2)

// Advance to the new stale time (500ms more to reach full 1000ms from last request)
vi.advanceTimersByTime(500)
await flushPromises()
// Now it should have triggered another request
expect(query).toHaveBeenCalledTimes(3)
})
})
Loading