Skip to content

Commit

Permalink
refactor: use global state
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Jan 13, 2025
1 parent 9a23f36 commit 680a2c6
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 34 deletions.
7 changes: 7 additions & 0 deletions src/data-loaders/navigation-guard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
DataLoaderPlugin,
NavigationResult,
DataLoaderPluginOptions,
useIsDataLoading,
} from 'unplugin-vue-router/data-loaders'
import { mockPromise } from '../../tests/utils'
import {
Expand Down Expand Up @@ -339,6 +340,12 @@ describe('navigation-guard', () => {
'does not call commit for a loader if the navigation is canceled by another loader'
)

it.todo('sets isDataLoading within a navigation', () => {
const { app } = setupApp({ isSSR: false })
const isGloballyLoading = app.runWithContext(() => useIsDataLoading())
expect(isGloballyLoading.value).toBe(false)
})

describe('signal', () => {
it('aborts the signal if the navigation throws', async () => {
setupApp({ isSSR: false })
Expand Down
65 changes: 31 additions & 34 deletions src/data-loaders/navigation-guard.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { isNavigationFailure } from 'vue-router'
import { effectScope, type App, type EffectScope } from 'vue'
import {
effectScope,
inject,
shallowRef,
type InjectionKey,
type ShallowRef,
type App,
type EffectScope,
} from 'vue'
import {
ABORT_CONTROLLER_KEY,
APP_KEY,
Expand All @@ -18,7 +26,12 @@ import type {
} from 'vue-router'
import { type _Awaitable } from '../utils'
import { toLazyValue, type UseDataLoader } from './createDataLoader'
import { shallowRef, watch } from 'vue'

/**
* Key to inject the global loading state for loaders used in `useIsDataLoading`.
* @internal
*/
export const IS_DATA_LOADING_KEY = Symbol() as InjectionKey<ShallowRef<boolean>>

/**
* TODO: export functions that allow preloading outside of a navigation guard
Expand All @@ -35,7 +48,7 @@ import { shallowRef, watch } from 'vue'
export function setupLoaderGuard({
router,
app,
effect,
effect: scope,
isSSR,
errors: globalErrors = [],
selectNavigationResult = (results) => results[0]!.value,
Expand Down Expand Up @@ -65,6 +78,10 @@ export function setupLoaderGuard({

router[IS_SSR_KEY] = !!isSSR

// global loading state for loaders used in `useIsDataLoading`
const isDataLoading = scope.run(() => shallowRef(false))!
app.provide(IS_DATA_LOADING_KEY, isDataLoading)

// guard to add the loaders to the meta property
const removeLoaderGuard = router.beforeEach((to) => {
// Abort any pending navigation. For cancelled navigations, this will happen before the `router.afterEach()`
Expand Down Expand Up @@ -157,6 +174,9 @@ export function setupLoaderGuard({

// unset the context so all loaders are executed as root loaders
setCurrentContext([])

isDataLoading.value = true

return Promise.all(
loaders.map((loader) => {
const { server, lazy, errors } = loader._.options
Expand All @@ -165,7 +185,7 @@ export function setupLoaderGuard({
return
}
// keep track of loaders that should be committed after all loaders are done
const ret = effect.run(() =>
const ret = scope.run(() =>
app
// allows inject and provide APIs
.runWithContext(() =>
Expand Down Expand Up @@ -226,6 +246,7 @@ export function setupLoaderGuard({
// unset the context so mounting happens without an active context
// and loaders do not believe they are being called as nested when they are not
setCurrentContext([])
isDataLoading.value = false
})
})

Expand Down Expand Up @@ -416,34 +437,10 @@ export interface DataLoaderPluginOptions {
errors?: Array<new (...args: any) => any> | ((reason?: unknown) => boolean)
}

export function useIsDataLoading(router: Router) {
const isLoading = shallowRef(false)

watch(
() => {
// read the current route
const currentRoute = router.currentRoute.value
if (!currentRoute) {
return []
}

// extract the loaders from route meta
const loaders = currentRoute.meta[LOADER_SET_KEY] || new Set()

// map each loader to its isLoading.value
return Array.from(loaders).map((loader) => {
const entry = loader._.getEntry(router)
return entry?.isLoading.value ?? false
})
},
(loaderStates) => {
// if any loader is true, isLoading becomes true
isLoading.value = loaderStates.some((state) => state)
},
{
immediate: true,
}
)

return isLoading
/**
* Return a ref that reflects the global loading state of all loaders within a navigation.
* This state doesn't update if `refresh()` is manually called.
*/
export function useIsDataLoading() {
return inject(IS_DATA_LOADING_KEY)!
}

0 comments on commit 680a2c6

Please sign in to comment.