Skip to content

Commit

Permalink
feat(utils): add computedWhen function
Browse files Browse the repository at this point in the history
  • Loading branch information
kiaking committed Jan 24, 2023
1 parent 82a34e6 commit d1ac49f
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 0 deletions.
56 changes: 56 additions & 0 deletions docs/composables/utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,62 @@

`Utils` composable module provides varias helper functions to create reactivity related objects.

## `WhenCondition`

A type alias of `MaybeComputedRef` from `@vueuse/core`. Used in "when" related utilities.

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

type WhenCondition<T> = MaybeComputedRef<T>
```
## `computedWhen`
Creates `computed` object with the given closure only when the condition is met. Useful when you want for example wait until api call resolves, and then perform some computing.
```ts
import { ComputedRef } from 'vue'

export function computedWhen<T, C>(
condition: WhenCondition<C>,
fn: (item: NonNullable<C>) => T
): ComputedRef<T | undefined>

export function computedWhen<T, C, D>(
condition: WhenCondition<C>,
fn: (item: NonNullable<C>) => T,
whenFalse: D
): ComputedRef<T | D>
```

```ts
import { computedWhen } from '@globalbrain/sefirot/lib/composables/Utils'
// Data is initially `undefined`.
const { data } = apiCall('...')
// The closure gets called only when `data` is truthy. The arg
// of the closure is the non-nullable value of the condition.
// The value when condition is falthy is `undefined`.
const isEmpty = computedWhen(data, (d) => {
// `d` is non nullalbe, and unwrapped if it's a Ref object.
return d.length === 0
})
```

You may also pass 3rd argument as the default value returned when the condition is falthy.

```ts
import { computedWhen } from '@globalbrain/sefirot/lib/composables/Utils'
const c = computedWhen(condition, () => {
return /* ... */
}, 'Default Value')
c.value // <- 'Default Value'
```

## `computedArray`

Shorthand function for creating `computed` object that returns an array. Useful when array items might change depending on some condition.
Expand Down
26 changes: 26 additions & 0 deletions lib/composables/Utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
import { MaybeComputedRef, resolveUnref } from '@vueuse/core'
import { ComputedRef, computed } from 'vue'

export type WhenCondition<T> = MaybeComputedRef<T>

export function computedWhen<T, C>(
condition: WhenCondition<C>,
fn: (item: NonNullable<C>) => T
): ComputedRef<T | undefined>

export function computedWhen<T, C, D>(
condition: WhenCondition<C>,
fn: (item: NonNullable<C>) => T,
whenFalse: D
): ComputedRef<T | D>

export function computedWhen<T, C, D>(
condition: WhenCondition<C>,
fn: (item: NonNullable<C>) => T,
whenFalse?: D
): ComputedRef<T | D> {
return computed(() => {
const c = resolveUnref(condition)

return c ? fn(c) : whenFalse as D
})
}

export function computedArray<T = any>(fn: (arr: T[]) => void): ComputedRef<T[]> {
return computed(() => {
const arr = [] as T[]
Expand Down
40 changes: 40 additions & 0 deletions tests/composables/Utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,46 @@ import * as Utils from 'sefirot/composables/Utils'
import { ref } from 'vue'

describe('composables/Utils', () => {
describe('computedWhen', () => {
test('it creates computed value only when the condition is met', () => {
const condition = ref(false)
const state = ref('initial')

const c = Utils.computedWhen(condition, () => {
return state.value
}, 'default')

// Default value.
expect(c.value).toBe('default')

// Condition is met.
condition.value = true
expect(c.value).toBe('initial')

// It's reactive value.
state.value = 'updated'
expect(c.value).toBe('updated')
})

test('when omitted, the default value when condition is false is `undefined`', () => {
const c = Utils.computedWhen(false, () => true)

expect(c.value).toBe(undefined)
})

test('it passes non-nullable value of condition to the closure arg', () => {
const value = ref<string | null>(null)

const c = Utils.computedWhen(value, (v) => v)

expect(c.value).toBe(undefined)

value.value = 'abc'

expect(c.value).toBe('abc')
})
})

describe('computedArray', () => {
test('computed array can be created', () => {
const state = ref(true)
Expand Down

0 comments on commit d1ac49f

Please sign in to comment.