- Notifications
You must be signed in to change notification settings - Fork7
🕶 Data fetching with Vue Composition API. Power of conditions to easily control and sync to the URL query string.
License
runkids/vue-condition-watcher
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
English |中文
vue-condition-watcher
is a data fetching library using the Vue Composition API. It allows you to easily control and sync data fetching to the URL query string using conditions.
requires Node.js 12.0.0 or higher.
✔ Automatically fetches data when conditions change.✔ Filters out null, undefined, [], and '' before sending requests.✔ Initializes conditions based on URL query strings and syncs them accordingly.✔ Synchronizes URL query strings with condition changes, maintaining normal navigation.✔ Ensures requests are first in, first out, and avoids repeats.✔ Handles dependent requests before updating data.✔ Customizable paging logic.✔ Refetches data when the page is refocused or network resumes.✔ Supports polling with adjustable periods.Caches data for faster rendering.✔ Allows manual data modifications to improve user experience.✔ TypeScript support.✔ Compatible with Vue 2 & 3 viavue-demi.
- Installation
- Quick Start
- Configs
- Return Values
- Execute Fetch
- Prevent Request
- Manually Trigger
- Intercepting Request
- Mutations data
- Conditions Change Event
- Fetch Event
- Polling
- Cache
- History Mode
- Lifecycle
- Pagination
- Changelog
cd examples/vue3yarn yarn serve
cd examples/vue2yarn yarn serve
yarn add vue-condition-watcher
or
npm install vue-condition-watcher
or via CDN
<scriptsrc="https://unpkg.com/vue-condition-watcher/dist/index.js"></script>
Example usingaxios
andvue-router
:
<scriptsetup>importaxiosfrom'axios'import{useRouter}from'vue-router'import{useConditionWatcher}from'vue-condition-watcher'constfetcher=params=>axios.get('/user/',{params})constrouter=useRouter()const{ conditions, data, loading, execute, error}=useConditionWatcher({ fetcher,conditions:{name:''},history:{sync:router}})</script><template><divclass="filter"><inputv-model="conditions.name"><button@click="execute">Refetch</button></div><divclass="container"> {{ !loading ? data : 'Loading...' }}</div><divv-if="error">{{ error }}</div></template>
fetcher
(required): Function for data fetching.conditions
(required): Default conditions.defaultParams
: Parameters preset with each request.initialData
: Initial data returned.immediate
: If false, data will not be fetched initially.manual
: If true, fetch requests are manual.history
: Syncs conditions with URL query strings using vue-router.pollingInterval
: Enables polling with adjustable intervals.pollingWhenHidden
: Continues polling when the page loses focus.pollingWhenOffline
: Continues polling when offline.revalidateOnFocus
: Refetches data when the page regains focus.cacheProvider
: Customizable cache provider.beforeFetch
: Modify conditions before fetching.afterFetch
: Adjust data before updating.onFetchError
: Handle fetch errors.
conditions
: Reactive object for conditions.data
: Readonly data returned by fetcher.error
: Readonly fetch error.isFetching
: Readonly fetch status.loading
: true when data and error are null.execute
: Function to trigger a fetch request.mutate
: Function to modify data.resetConditions
: Resets conditions to initial values.onConditionsChange
: Event triggered on condition changes.onFetchSuccess
: Event triggered on successful fetch.onFetchError
: Event triggered on fetch error.onFetchFinally
: Event triggered when fetch ends.
Fetch data whenconditions
change:
const{ conditions}=useConditionWatcher({ fetcher,conditions:{page:0},defaultParams:{opt_expand:'date'}})conditions.page=1conditions.page=2
Manually trigger a fetch:
const{ conditions,execute:refetch}=useConditionWatcher({ fetcher,conditions:{page:0},defaultParams:{opt_expand:'date'}})refetch()
Force reset conditions:
const{ conditions, resetConditions}=useConditionWatcher({const{ conditions, resetConditions}=useConditionWatcher({ fetcher,immediate:false,conditions:{page:0,name:'',date:[]},})resetConditions({name:'runkids',date:['2022-01-01','2022-01-02']})
Prevent requests until execute is called:
const{ execute}=useConditionWatcher({ fetcher, conditions,immediate:false,})execute()
Disable automatic fetch and use execute() to trigger:
const{ execute}=useConditionWatcher({ fetcher, conditions,manual:true,})execute()
Modify conditions before fetch:
useConditionWatcher({ fetcher,conditions:{date:['2022/01/01','2022/01/02']},initialData:[],asyncbeforeFetch(conds,cancel){awaitcheckToken()const{ date, ...baseConditions}=condsconst[after,before]=datebaseConditions.created_at_after=afterbaseConditions.created_at_before=beforereturnbaseConditions}})
Modify data after fetch:
const{ data}=useConditionWatcher({ fetcher, conditions,asyncafterFetch(response){if(response.data===null){return[]}constfinalResponse=awaitotherAPIById(response.data.id)returnfinalResponse}})
Handle fetch errors:
const{ data, error}=useConditionWatcher({ fetcher, conditions,asynconFetchError({data, error}){if(error.code===401){awaitdoSomething()}return{data:[],error:'Error Message'}}})
Update data using mutate function:
mutate(newData)
Update part of data:
constfinalData=mutate(draft=>{draft[0].name='runkids'returndraft})
const{ conditions, data, mutate}=useConditionWatcher({fetcher:api.userInfo, conditions,initialData:[]})asyncfunctionupdateUserName(userId,newName,rowIndex=0){constresponse=awaitapi.updateUer(userId,newName)mutate(draft=>{draft[rowIndex]=response.datareturndraft})}
Handle condition changes:
const{ conditions, onConditionsChange}=useConditionWatcher({ fetcher,conditions:{page:0},})conditions.page=1onConditionsChange((conditions,preConditions)=>{console.log(conditions)console.log(preConditions)})
Handle fetch events:
const{ onFetchResponse, onFetchError, onFetchFinally}=useConditionWatcher(config)onFetchResponse(response=>console.log(response))onFetchError(error=>console.error(error))onFetchFinally(()=>{//todo})
Enable polling:
useConditionWatcher({ fetcher, conditions,pollingInterval:1000})
Use ref for reactivity:
constpollingInterval=ref(0)useConditionWatcher({ fetcher, conditions,pollingInterval:pollingInterval})onMounted(()=>pollingInterval.value=1000)
Continue polling when hidden or offline:
useConditionWatcher({ fetcher, conditions,pollingInterval:1000,pollingWhenHidden:true,// pollingWhenHidden default is falsepollingWhenOffline:true,// pollingWhenOffline default is falserevalidateOnFocus:true// revalidateOnFocus default is false})
Cache data globally:
// App.vueconstcache=newMap()exportdefault{name:'App',provide:{cacheProvider:()=>cache}}useConditionWatcher({ fetcher, conditions,cacheProvider:inject('cacheProvider')})
Cache data inlocalStorage
:
functionlocalStorageProvider(){constmap=newMap(JSON.parse(localStorage.getItem('your-cache-key')||'[]'))window.addEventListener('beforeunload',()=>{constappCache=JSON.stringify(Array.from(map.entries()))localStorage.setItem('your-cache-key',appCache)})returnmap}useConditionWatcher({ fetcher, conditions,cacheProvider:localStorageProvider})
Enable history mode usingvue-router
:
constrouter=useRouter()useConditionWatcher({ fetcher, conditions,history:{sync:router}})
Exclude keys from URL query string:
constrouter=useRouter()useConditionWatcher({ fetcher,conditions:{users:['runkids','hello'],limit:20,offset:0},history:{sync:router,ignore:['limit']}})// the query string will be ?offset=0&users=runkids,hello
Convert conditions to query strings:
conditions:{ users:['runkids','hello'] company:'' limit:20,offset:0}// the query string will be ?offset=0&limit=20&users=runkids,hello
Sync query strings to conditions on page refresh:
URL query string: ?offset=0&limit=10&users=runkids,hello&compay=vue
conditions
will become
{users:['runkids','hello'],company:'vue',limit:10,offset:0}
Use navigation to replace or push current location:
useConditionWatcher({ fetcher,conditions:{limit:20,offset:0},history:{sync:router,navigation:'replace'}})
Fires new and old condition values.
onConditionsChange((cond,preCond)=>{console.log(cond)console.log(preCond)})
Modify conditions before fetch or stop fetch.
const{ conditions}=useConditionWatcher({ fetcher, conditions, beforeFetch})asyncfunctionbeforeFetch(cond,cancel){if(!cond.token){// stop fetchcancel()// will fire onConditionsChange againconditions.token=awaitfetchToken()}returncond})
afterFetch
fire beforeonFetchSuccess
afterFetch
can modify data before update.Type Modify data before update Dependent request afterFetch config ⭕️ ⭕️ onFetchSuccess event ❌ ❌ <template> {{ data?.detail }}<!-- 'xxx' --></template>
const{ data, onFetchSuccess}=useConditionWatcher({ fetcher, conditions,asyncafterFetch(response){//response = { id: 1 }constdetail=awaitfetchDataById(response.id)returndetail// { id: 1, detail: 'xxx' }})})onFetchSuccess((response)=>{console.log(response)// { id: 1, detail: 'xxx' }})
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})
Will fire on fetch finished.
onFetchFinally(async()=>{//do something})
You might need to reuse the data in many places. It is incredibly easy to create reusable hooks ofvue-condition-watcher
:
functionuseUserExpensesHistory(id){const{ conditions, data, error, loading}=useConditionWatcher({fetcher:params=>api.user(id,{ params}),defaultParams:{opt_expand:'amount,place'},conditions:{daterange:[]}immediate:false,initialData:[],beforeFetch(cond,cancel){if(!id){cancel()}const{ daterange, ...baseCond}=condif(daterange.length){[baseCond.created_at_after,baseCond.created_at_before]=[daterange[0],daterange[1]]}returnbaseCond}})return{histories:data,isFetching:loading,isError:error,daterange:conditions.daterange}}
Use in components:
<scriptsetup> const{daterange,histories,isFetching,isError} = useUserExpensesHistory(route.params.id) onMounted(() =>{//start first time data fetching after initial date rangedaterange=[newDate(),newDate()]})</script>
<template><el-date-pickerv-model="daterange":disabled="isFetching"type="daterange"/><divv-for="history in histories":key="history.id"> {{ `${history.created_at}: ${history.amount}` }}</div></template>
Congratulations! 🥳 You have learned how to use composition-api withvue-condition-watcher
.
Now we can manage the paging information usevue-condition-watcher
.
Here is an example use Django the limit and offset functions and Element UI.
CreateusePagination
hook:
functionusePagination(){letcancelFlag=falseconst{ startLoading, stopLoading}=useLoading()constrouter=useRouter()const{ conditions, data, execute, resetConditions, onConditionsChange, onFetchFinally}=useConditionWatcher({fetcher:api.list,conditions:{daterange:[],limit:20,offset:0},immediate:true,initialData:[],history:{sync:router,ignore:['limit']}, beforeFetch})constcurrentPage=computed({get:()=>conditions.offset/conditions.limit+1,set:(page)=>conditions.offset=(page-1)*conditions.limit})onConditionsChange((newCond,oldCond)=>{if(newCond.offset!==0&&newCond.offset===oldCond.offset){cancelFlag=trueconditions.offset=0}})asyncfunctionbeforeFetch(cond,cancel){if(cancelFlag){cancel()cancelFlag=falsereturncond}awaitnextTick()startLoading()const{ daterange, ...baseCond}=condif(daterange.length){[baseCond.created_at_after,baseCond.created_at_before]=[daterange[0],daterange[1]]}returnbaseCond}onFetchFinally(async()=>{awaitnextTick()stopLoading()window.scrollTo(0,0)})return{ data, conditions, currentPage, resetConditions,refetch:execute}}
Use in components:
<scriptsetup> const{data,conditions,currentPage,resetConditions,refetch} = usePagination()</script>
<template><el-button@click="refetch">Refetch Data</el-button><el-button@click="resetConditions">Reset Offset</el-button><el-date-pickerv-model="conditions.daterange"type="daterange"/><divv-for="info in data":key="info.id"> {{ info }}</div><el-paginationv-model:currentPage="currentPage"v-model:page-size="conditions.limit":total="data.length"/></template>
Reset offset when daterange or limit changes.
- Error Retry
- Nuxt SSR SSG Support
Inspired byvercel/swr
MIT License © 2020-PRESENTRunkids
About
🕶 Data fetching with Vue Composition API. Power of conditions to easily control and sync to the URL query string.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors3
Uh oh!
There was an error while loading.Please reload this page.