Vue Composition API for automatic fetch data when condition has been changed
vue-condition-watcher
Vue Composition API for automatic fetch data when condition has been changed
requires Node.js 12.0.0 or higher.
Features
✔ Auto fetch data when conditions changed.
✔ Auto filter falsy value in conditions.
✔ Auto converts the corresponding type. (string, number, array, date)
✔ Store the conditions within the URL hash every time a condition is changed
✔ Sync the state with the query string and initialize off of that and that back/forward/execute work.
✔ Keep requests first in — first out.
✔ Works for Vue 2 & 3 by the power of vue-demi
? Download Vue3 example here (Use Vite)
cd examples/vue3
yarn
yarn serve
? Download Vue2 @vue/composition-api example here
cd examples/vue2
yarn
yarn serve
? Online demo with vue-infinite-scroll
Quick Start
Simple example for vue-next
and vue-router-next
createApp({
template: `
<div class="filter">
<input v-model="conditions.name">
<button @click="execute">Refetch</button>
</div>
<div class="container" v-if="!loading">
{{ data }}
</div>
<div class="loading" v-else>Loading...</div>
`,
setup() {
const config = {
fetcher: params => axios.get('/user/', {params}),
conditions: {
name: ''
},
}
return useConditionWatcher(config, {sync: 'router'})
},
})
.provide('router', router)
.use(router)
.mount(document.createElement('div'))
Usage
In your project
yarn add vue-condition-watcher
Or with npm
npm install vue-condition-watcher
CDN
https://unpkg.com/vue-condition-watcher/dist/index.js
Basic Usage
const { conditions, data, error, loading, execute, resetConditions, onConditionsChange } = useConditionWatcher(config, queryOptions)
Execute Fetch
conditions
is reactive proxy, easy execute fetch when conditions
value changed
const { conditions } = useConditionWatcher({
fetcher,
conditions: {
page: 0
},
defaultParams: {
opt_expand: 'date'
}
})
conditions.page = 1 // fetch data with payload { page: 1, opt_expand: 'date' }
conditions.page = 2 // fetch data with payload { page: 2, opt_expand: 'date' }
Just call execute
function to send a request if you need.
const { conditions, execute: refetch } = useConditionWatcher({
fetcher,
conditions: {
page: 0
},
defaultParams: {
opt_expand: 'date'
}
})
refetch() // fetch data with payload { page: 0, opt_expand: 'date' }
Update conditions one time.
const { conditions, resetConditions } = useConditionWatcher({
fetcher,
immediate: false,
conditions: {
page: 0,
name: '',
date: []
},
})
// initial conditions then fire onConditionsChange event
Object.assign(conditions, {
name: 'runkids',
date: ['2022-01-01', '2022-01-02']
})
// Reset conditions
function reset () {
Object.assign(conditions, {
page: 0,
name: '',
date: []
})
// Or you can just use `resetConditions` function to initial value.
resetConditions()
}
Conditions Change Event
onConditionsChange
can help you handle conditions changed.
Will return new value and old value.
const { conditions, onConditionsChange } = useConditionWatcher({
fetcher,
conditions: {
page: 0
},
})
conditions.page = 1
onConditionsChange((conditions, preConditions)=> {
console.log(conditions) // { page: 1}
console.log(preConditions) // { page: 0}
})
Fetch Event
The onFetchResponse
, onFetchError
and onFetchFinally
will fire on fetch request.
const { onFetchResponse, onFetchError, onFetchFinally } = useConditionWatcher(config)
onFetchResponse((response) => {
console.log(response)
})
onFetchError((error) => {
console.error(error)
})
onFetchFinally(() => {
//todo
})
Prevent Request
Setting the immediate
to false will prevent the request until the execute
function called or conditions changed.
const { execute } = useConditionWatcher({
fetcher,
conditions,
immediate: false,
})
execute()
Intercepting Request
The beforeFetch
let you modify conditions before fetch, or you can call cancel
function to stop fetch.
useConditionWatcher({
fetcher,
conditions: {
date: ['2022/01/01', '2022/01/02']
},
initialData: [],
async beforeFetch(conditions, cancel) {
// await something
await doSomething ()
// conditions is an object clone copy from config.conditions
const {date, ...baseConditions} = conditions
const [after, before] = date
baseConditions.created_at_after = after
baseConditions.created_at_before = before
return baseConditions
}
})
The afterFetch
can intercept the response before data updated
const { data } = useConditionWatcher({
fetcher,
conditions,
async afterFetch(response) {
//response.data = {id: 1, name: 'runkids'}
if(response.data === null) {
return []
}
const finalResponse = await otherAPIById(response.data.id)
return finalResponse // [{message: 'Hello', sender: 'runkids'}]
}
})
console.log(data) //[{message: 'Hello', sender: 'runkids'}]
The onFetchError
can intercept the response before data and error updated
const { data, error } = useConditionWatcher({
fetcher,
conditions,
async onFetchError({data, error}) {
if(error.code === 401) {
await doSomething()
}
return {
data: [],
error: 'Error Message'
}
}
})
console.log(data) //[]
console.log(error) //'Error Message'
More Configs
-
config
: An object of config for vue-condition-watcher-
fetcher
(⚠️Required) : Can be any asynchronous function to fetch data -
conditions
(⚠️Required) : An object of conditions, also be initial value -
defaultParams
: An object of fetcher's default
parameters -
initialData
:data
default value is null, and you can settingdata
default value by use this config -
immediate
: Setting theimmediate
to false will prevent the request until theexecute
function called.immediate
default istrue
.const config = { fetcher: params => axios.get('url', { params }), defaultParams: { type: 'member' }, immediate: true, initialData: [] conditions: { offset: 0, limit: 10, username: '', }, }
-
-
queryOptions
: An object of options to sync query string with conditions-
⚠️
queryOptions
work base on vue-router, you need install vue-router first. -
sync
: key of provide name ( String | Symbol )- main.js: register router
import {createApp} from 'vue' import App from './App.vue' import { router } from './router' const app = createApp(App) .provide('router', router) // it's should be required .use(router) .mount('#app')
- then
useConditionWatcher(config, {sync: 'router'})
-
ignore
: you can ignore key name from conditions, will not push with query.useConditionWatcher(config, {sync: 'router', ignore: ['offset', 'limit']})
-
navigation
: use vue router navigation method push or replace, default value is push.useConditionWatcher(config, {sync: 'router', navigation: 'replace'})
-
How to use in vue@2 with @vue/composition-api
-
( Good ) Add
provide
inmain.js
new Vue({ el: '#app', router, store, provide: { router }, render: h => h(App) })
-
Add
provide
in current fileimport { useConditionWatcher } from "vue-condition-watcher"; import { provide } from "@vue/composition-api"; import router from "@/router"; import api from "../api"; export default { setup() { provide("router", router); const config = { fetcher: api.users, conditions: { offset: 0, limit: 9 } }; return useConditionWatcher(config, {sync: 'router', ignore: ['offset', 'limit']}); } };
How to use in Nuxt with @nuxtjs/composition-api
-
Add
provide
in current fileimport { useConditionWatcher } from "vue-condition-watcher"; import { defineComponent, useRoute, provide, useContext } from "@nuxtjs/composition-api"; import api from "~/api"; export default defineComponent({ setup() { const route = useRoute(); const { app } = useContext(); provide('router', app.router); const config = { fetcher: api.users, conditions: { offset: 0, limit: 9 } }; return useConditionWatcher(config, {sync: 'router', ignore: ['offset', 'limit']}); } });
Return Values
conditions
: An object and returns a reactive proxy of conditionsdata
: Data resolved byconfig.fetcher
error
: Error thrown byconfig.fetcher
loading
: Request is fetchingexecute
: The function to fetch dataresetConditions
: Reset conditions to initial valueonConditionsChange
: Will fire on conditions changedonFetchSuccess
: Will fire on fetch request successonFetchError
: Will fire on fetch request erroronFetchFinally
: Will fire on fetch finished
Lifecycle
-
onConditionsChange
Fire new conditions value and old conditions value.
onConditionsChange((cond, preCond)=> { console.log(cond) console.log(preCond) })
-
beforeFetch
You can modify conditions before fetch, or you can call second of arguments to stop fetch this time.
const { conditions } = useConditionWatcher({ fetcher, conditions, beforeFetch }) async function beforeFetch(cond, cancel){ if(!cond.token) { // stop fetch cancel() // will fire onConditionsChange again conditions.token = await fetchToken() } return cond })
-
afterFetch
&onFetchSuccess
afterFetch
fire beforeonFetchSuccess
afterFetch
can modify data before update.Type Modify data before update afterFetch config ⭕️ onFetchSuccess event ❌ <template> {{ data?.detail }} <!-- 'xxx' --> </template>
const { data, onFetchSuccess } = useConditionWatcher({ fetcher, conditions, async afterFetch(response){ //response = { id: 1 } const detail = await fetchDataById(response.id) return detail // { id: 1, detail: 'xxx' } }) }) onFetchSuccess((response)=> { console.log(response) // { id: 1, detail: 'xxx' } })
-
onFetchError(config)
&onFetchError(event)
config.onFetchError
fire beforeevent.onFetchError
config.onFetchError
can modify data and error before update.Type Modify data before update Modify error before update onFetchError config ⭕️ ⭕️ onFetchError event ❌ ❌ const { onFetchError } = useConditionWatcher({ fetcher, conditions, onFetchError(ctx){ return { data: [], error: 'Error message.' } }) }) onFetchError((error)=> { console.log(error) // origin error data })
-
onFetchFinally
Will fire on fetch finished.
onFetchFinally(async ()=> { //do something })