Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rxjs): Correct useObservable type signature and provide means of…
… creating a non-undefined Ref (#1551)
- Loading branch information
Showing
3 changed files
with
69 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import type { Observable } from 'rxjs' | ||
import { BehaviorSubject } from 'rxjs' | ||
import { delay } from 'rxjs/operators' | ||
import { useObservable } from '.' | ||
|
||
describe('useObservable', () => { | ||
let testDataSource: BehaviorSubject<TestPerson> | ||
let delayedEmissionStream: Observable<TestPerson> | ||
|
||
beforeEach(() => { | ||
testDataSource = new BehaviorSubject<TestPerson>({ fullName: 'Mario Mario' }) | ||
delayedEmissionStream = testDataSource.pipe(delay(0)) | ||
}) | ||
|
||
describe('when initialValue is not provided', () => { | ||
it('should set the ref\'s value to undefined before the stream has emitted', async() => { | ||
const testPerson = useObservable(delayedEmissionStream) | ||
expect(testPerson.value).toBe(undefined) | ||
}) | ||
|
||
it('should set the ref\'s value from the data emitted on the stream', async() => { | ||
const testPerson = useObservable(delayedEmissionStream) | ||
|
||
// wait for next tick, allowing RxJS to emit the value | ||
await new Promise(resolve => setTimeout(resolve, 0)) | ||
|
||
// Notice optional chaining operator required | ||
expect(testPerson.value?.fullName).toEqual('Mario Mario') | ||
}) | ||
}) | ||
|
||
describe('when initialValue is provided', () => { | ||
it('should set the ref\'s value to initialData before the stream has emitted', () => { | ||
const testPerson = useObservable(delayedEmissionStream, { initialValue: { fullName: 'I don\'t know yet!' } }) | ||
|
||
// Notice how we do not need the optional chaining to access fullName | ||
expect(testPerson.value.fullName).toBe('I don\'t know yet!') | ||
}) | ||
|
||
it('should set the ref\'s value from the data emitted on the stream', async() => { | ||
const testPerson = useObservable(delayedEmissionStream, { initialValue: { fullName: 'I don\'t know yet!' } }) | ||
// wait for next tick, allowing RxJS to emit the value | ||
await new Promise(resolve => setTimeout(resolve, 0)) | ||
|
||
// Notice how we do not need the optional chaining to access fullName | ||
expect(testPerson.value.fullName).toBe('Mario Mario') | ||
}) | ||
}) | ||
}) | ||
|
||
/** | ||
* Used for this test to examine optional chaining and typescript type computation. | ||
*/ | ||
interface TestPerson { | ||
fullName: string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,26 @@ | ||
import type { Observable } from 'rxjs' | ||
import type { Ref } from 'vue-demi' | ||
import type { Ref, UnwrapRef } from 'vue-demi' | ||
import { ref } from 'vue-demi' | ||
import { tryOnScopeDispose } from '@vueuse/shared' | ||
|
||
export interface UseObservableOptions { | ||
export interface UseObservableOptions<I> { | ||
onError?: (err: any) => void | ||
/** | ||
* The value that should be set if the observable has not emitted. | ||
*/ | ||
initialValue?: I | undefined | ||
} | ||
|
||
export function useObservable<H>(observable: Observable<H>, options?: UseObservableOptions): Readonly<Ref<H>> { | ||
const value = ref<H | undefined>() | ||
export function useObservable<H, I = undefined>( | ||
observable: Observable<H>, | ||
options?: UseObservableOptions<I | undefined>): Readonly<Ref<H | I>> { | ||
const value = ref<H | I | undefined>(options?.initialValue) | ||
const subscription = observable.subscribe({ | ||
next: val => (value.value = val), | ||
next: val => (value.value = (val as UnwrapRef<H>)), | ||
error: options?.onError, | ||
}) | ||
tryOnScopeDispose(() => { | ||
subscription.unsubscribe() | ||
}) | ||
return value as Readonly<Ref<H>> | ||
return value as Readonly<Ref<H | I>> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters