8000 feat(useMicroLoader): add useMicroLoader by osovv · Pull Request #4718 · vueuse/vueuse · GitHub
[go: up one dir, main page]

Skip to content

feat(useMicroLoader): add useMicroLoader #4718

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

Open
wants to merge 1 commit 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
1 change: 1 addition & 0 deletions packages/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export * from './useMediaControls'
export * from './useMediaQuery'
export * from './useMemoize'
export * from './useMemory'
export * from './useMicroLoader'
export * from './useMounted'
export * from './useMouse'
export * from './useMouseInElement'
Expand Down
57 changes: 57 additions & 0 deletions packages/core/useMicroLoader/components/CustomLoaderDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script setup lang="ts">
import { shallowRef } from 'vue'
import { useMicroLoader } from '../index'

const props = defineProps<{
taskDuration: number
minLoadingTimeMs: number
quickLoadingThresholdMs: number
}>()

const customTaskStatus = shallowRef<'idle' | 'pending' | 'done'>('idle')
const startTime = shallowRef<number | null>(null)
const currentTime = shallowRef<number | null>(null)

const isCustomLoading = useMicroLoader(() => customTaskStatus.value === 'pending', {
minLoadingTimeMs: props.minLoadingTimeMs,
quickLoadingThresholdMs: props.quickLoadingThresholdMs,
})

async function simulateCustomTask() {
customTaskStatus.value = 'pending'
startTime.value = Date.now()

const interval = setInterval(() => {
currentTime.value = Date.now()
}, 16)

await new Promise(resolve => setTimeout(resolve, props.taskDuration))

clearInterval(interval)
customTaskStatus.value = 'done'
startTime.value = null
currentTime.value = null
}
</script>

<template>
<div>
<div>
<p>Loading state: {{ isCustomLoading ? 'Loading...' : 'Not Loading' }}</p>
<p v-if="startTime">
Time elapsed: {{ ((currentTime || Date.now()) - startTime) / 1000 }}s
</p>
</div>

<button
:disabled="customTaskStatus === 'pending'"
@click="simulateCustomTask"
>
Run Custom Task
</button>
</div>
</template>

<style scoped>

</style>
103 changes: 103 additions & 0 deletions packages/core/useMicroLoader/demo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<script setup lang="ts">
import { computed, shallowRef } from 'vue'
import { useMicroLoader } from '.'
import CustomLoaderDemo from './components/CustomLoaderDemo.vue'

const quickTaskStatus = shallowRef<'idle' | 'pending' | 'done'>('idle')
const slowTaskStatus = shallowRef<'idle' | 'pending' | 'done'>('idle')

const taskDuration = shallowRef(500)
const minLoadingTime = shallowRef(2000)
const quickLoadingThreshold = shallowRef(1000)

const customLoaderKey = computed(() =>
`${taskDuration.value}-${minLoadingTime.value}-${quickLoadingThreshold.value}`,
)

const isQuickLoading = useMicroLoader(() => quickTaskStatus.value === 'pending')
const isSlowLoading = useMicroLoader(() => slowTaskStatus.value === 'pending')

async function simulateQuickTask() {
quickTaskStatus.value = 'pending'
await new Promise(resolve => setTimeout(resolve, 200))
quickTaskStatus.value = 'done'
}

async function simulateSlowTask() {
slowTaskStatus.value = 'pending'
await new Promise(resolve => setTimeout(resolve, 1500))
slowTaskStatus.value = 'done'
}
</script>

<template>
<div>
<div>
<h3>Quick Task (200ms)</h3>
<p>Loading state: {{ isQuickLoading ? 'Loading...' : 'Not Loading' }}</p>
<button
:disabled="quickTaskStatus === 'pending'"
@click="simulateQuickTask"
>
Run Quick Task
</button>
<p>Note: Loading indicator won't show due to quick execution</p>
</div>

<div>
<h3>Slow Task (1500ms)</h3>
<p>Loading state: {{ isSlowLoading ? 'Loading...' : 'Not Loading' }}</p>
<button
:disabled="slowTaskStatus === 'pending'"
@click="simulateSlowTask"
>
Run Slow Task
</button>
<p>Note: Loading indicator will show due to slow execution</p>
</div>

<div>
<h3>Custom Configuration</h3>
<div>
<label>
Task Duration (ms):
<input
v-model="taskDuration"
type="number"
min="0"
step="100"
>
</label>
</div>
<div>
<label>
Min Loading Time (ms):
<input
v-model="minLoadingTime"
type="number"
min="0"
step="100"
>
</label>
</div>
<div>
<label>
Quick Loading Threshold (ms):
<input
v-model="quickLoadingThreshold"
type="number"
min="0"
step="100"
>
</label>
</div>

<CustomLoaderDemo
:key="customLoaderKey"
:task-duration="taskDuration"
:min-loading-time-ms="minLoadingTime"
:quick-loading-threshold-ms="quickLoadingThreshold"
/>
</div>
</div>
</template>
87 changes: 87 additions & 0 deletions packages/core/useMicroLoader/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
category: State
---

# useMicroLoader

A composable that provides a smart loading state management with micro-loading detection. It helps prevent UI flicker for quick operations while ensuring a minimum loading time for better user experience. Particularly useful for handling both fast and slow network conditions, preventing unnecessary loading indicators on fast connections.

## Usage

```ts
import { useMicroLoader } from '@vueuse/core'

const isLoading = useMicroLoader(() => asyncTaskStatus.value === 'pending')
```

## Features

- Prevents UI flicker for quick operations (under `quickLoadingThresholdMs`)
- Ensures a minimum loading time for better UX
- Adapts to network conditions - won't show loading indicators on fast connections
- Handles both local operations and network requests efficiently

## Parameters

- `isLoadingRef`: A reactive reference or getter that indicates the loading state
- `options`: Optional configuration object
- `minLoadingTimeMs`: Minimum duration (in milliseconds) that the loading state should be shown (default: 500ms)
- `quickLoadingThresholdMs`: Time threshold (in milliseconds) below which loading state won't be shown (default: 300ms)

## Return Value

Returns a reactive reference that indicates whether the micro-loading state is active.

## Examples

### Basic Usage

```ts
const isLoading = ref(false)
const isMicroLoading = useMicroLoader(isLoading)
```

### Custom Configuration

```ts
const isLoading = ref(false)
const isMicroLoading = useMicroLoader(isLoading, {
minLoadingTimeMs: 1000, // Show loading for at least 1 second
quickLoadingThresholdMs: 500 // Don't show loading for operations under 500ms
})
```

### With Async Operations

```ts
const taskStatus = ref<'idle' | 'pending' | 'done'>('idle')
const isMicroLoading = useMicroLoader(() => taskStatus.value === 'pending')

async function performTask() {
taskStatus.value = 'pending'
await someAsyncOperation()
taskStatus.value = 'done'
}
```

### Network-Aware Loading

```ts
const taskStatus = ref<'idle' | 'pending' | 'done'>('idle')
const isMicroLoading = useMicroLoader(() => taskStatus.value === 'pending', {
// On fast networks, don't show loading for operations under 1 second
quickLoadingThresholdMs: 1000,
// Ensure loading state is shown for at least 500ms on slow networks
minLoadingTimeMs: 500,
})

async function fetchData() {
taskStatus.value = 'pending'
try {
await fetch('/api/data')
}
finally {
taskStatus.value = 'done'
}
}
```
Loading
0