Skip to content

Commit

Permalink
refactor(react): remove useStream hook
Browse files Browse the repository at this point in the history
Move the useStream hook into the connect module, and do not expose it through any index file.

BREAKING CHANGE: Removes useStream hook. Use connect instead.
  • Loading branch information
tlaundal committed Apr 16, 2020
1 parent 4f57a28 commit b25407a
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 121 deletions.
55 changes: 53 additions & 2 deletions src/react/connect.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import testUntyped from 'ava';
import { act, create, ReactTestRenderer } from 'react-test-renderer';
import React from 'react';
import { empty, of, BehaviorSubject } from 'rxjs';
import { connect } from './connect';
import { empty, of, BehaviorSubject, Subject } from 'rxjs';
import { connect, useStream, NOT_YET_EMITTED } from './connect';
import { spy, SinonSpy } from 'sinon';
import { TestInterface } from 'ava';
import { renderHook } from '@testing-library/react-hooks';

type Props = { msg: string; num?: number };
const initialProps = { msg: 'hello' };
Expand All @@ -27,6 +28,56 @@ test.beforeEach((t) => {

test.afterEach((t) => t.context.WrappedComponentSpy.resetHistory());

test('useStream return initial value right away', (t) => {
const { result } = renderHook(() => useStream(empty()));

t.deepEqual(result.current, NOT_YET_EMITTED);
});

test('useStream return value from stream', (t) => {
const source$ = new Subject<Props>();
const { result } = renderHook(() => useStream(source$));

source$.next(initialProps);

t.deepEqual(result.current, initialProps);
});

test('useStream unsubscribes on unmount', (t) => {
const source$ = new Subject<string>();
const { unmount } = renderHook(() => useStream(source$));

unmount();

t.deepEqual(source$.observers, []);
});

test('useStream does not resubscribe on rerender', (t) => {
const source$ = new Subject<string>();
const { rerender } = renderHook(() => useStream(source$));

const subscription = source$.observers[0];
rerender();

t.assert(source$.observers[0] === subscription);
});

test('useStream unsubscribes, keeps latest value and subscribes new stream', (t) => {
const alpha = new Subject<Props>();
const bravo = new Subject<Props>();

const { rerender, result } = renderHook(({ source$ }) => useStream(source$), {
initialProps: { source$: alpha },
});

alpha.next(initialProps);
rerender({ source$: bravo });

t.deepEqual(result.current, initialProps);
t.deepEqual(alpha.observers, []);
t.deepEqual(bravo.observers.length, 1);
});

test('connect should render null on first render', (t) => {
const { WrappedComponentSpy } = t.context;
const HOComponent = connect(WrappedComponentSpy, empty());
Expand Down
27 changes: 25 additions & 2 deletions src/react/connect.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
import { useStream, NOT_YET_EMITTED } from './useStream';
import { Observable } from 'rxjs';
import React, { ComponentType } from 'react';
import React, { ComponentType, useState, useEffect } from 'react';

export const NOT_YET_EMITTED = Symbol('Returned from rxbeach/react:useStream');
export type NOT_YET_EMITTED = typeof NOT_YET_EMITTED;

/**
* React hook to subscribe to a stream
*
* Each emit from the stream will make the component re-render with the new
* value. Initially, `NOT_YET_EMITTED` is returned, because an `Observable`
* has no guarantee for when the first emit will happen.
*
* @param source$ Stream that provides the needed values
*/
export const useStream = <T>(source$: Observable<T>): T | NOT_YET_EMITTED => {
const [value, setValue] = useState<T | NOT_YET_EMITTED>(NOT_YET_EMITTED);

useEffect(() => {
const subscription = source$.subscribe(setValue);

return () => subscription.unsubscribe();
}, [source$]);

return value;
};

/**
* Higher order component for connecting a component to a stream
Expand Down
1 change: 0 additions & 1 deletion src/react/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { useStream, NOT_YET_EMITTED } from './useStream';
export { connect } from './connect';
56 changes: 0 additions & 56 deletions src/react/useStream.spec.ts

This file was deleted.

60 changes: 0 additions & 60 deletions src/react/useStream.ts

This file was deleted.

0 comments on commit b25407a

Please sign in to comment.