From e9d429f1cd2846b0cc5305e399d8a52df60e4e8d Mon Sep 17 00:00:00 2001 From: Josep M Sobrepere Date: Tue, 21 Jul 2020 00:45:55 +0200 Subject: [PATCH] docs(monorepo): update README files --- README.md | 67 +-------------- packages/core/README.md | 127 +++++++++++++++++++++++++++++ packages/dom/README.md | 24 ++++++ packages/dom/src/batchUpdates.ts | 6 +- packages/utils/README.md | 119 +++++++++++++++++++++++++++ packages/utils/src/groupInMap.ts | 4 +- packages/utils/src/mergeWithKey.ts | 6 +- 7 files changed, 280 insertions(+), 73 deletions(-) create mode 100644 packages/core/README.md create mode 100644 packages/dom/README.md create mode 100644 packages/utils/README.md diff --git a/README.md b/README.md index a42ca15..4a0cc6a 100644 --- a/README.md +++ b/README.md @@ -32,15 +32,11 @@ - [suspend](#suspend) - [suspended](#suspended) - [switchMapSuspended](#switchmapsuspended) - - Utils - - [subjectFactory](#subjectfactory) - - [useSubscribe](#usesubscribe) - - [Subscribe](#subscribe) - [Examples](#examples) ## Installation - npm install react-rxjs + npm install @react-rxjs/core ## API @@ -165,67 +161,6 @@ const story$ = selectedStoryId$.pipe( Like `switchMap` but applying a `startWith(SUSPENSE)` to the inner observable. -### subjectFactory - -```tsx -const getCounterActions$ = subjectFactory() - -const onInc = (id: string) => getCounterActions$(id).next('INC') -const onDec = (id: string) => getCounterActions$(id).next('DEC') - -const useCounter = connectFactoryObservable( - (id: string) => getCounterActions$(id).pipe( - map(type => type === 'INC' ? 1 : -1) - startWith(0), - scan((a, b) => a + b) - ) -) - -const Counter: React.FC<{id: string}> = ({id}) => { - const counter = useCounter(id); - return ( - <> - - {counter} - - - ) -} -``` - -Creates a pool of Subjects identified by key, and returns: -- A function that accepts a key and returns the Subject linked to that key. - -Strictly speaking the returned value is not a real Subject. It's in fact a -multicasted Observable that it's also an Observer. That's because in order to -prevent memory-leaks this cached Observable will be removed from the cache when -it finalizes. - -### useSubscribe - -A React hook that creates a subscription to the provided observable once the -component mounts and it unsubscribes when the component unmounts. - -Arguments: - - `source$`: Source observable that the hook will subscribe to. - - `unsubscribeGraceTime`: Amount of time in ms that the hook should wait before - unsubscribing from the source observable after it unmounts (default = 200). - -Important: This hook doesn't trigger any updates. - -### Subscribe - -A React Component that creates a subscription to the provided observable once -the component mounts and it unsubscribes from it when the component unmounts. - -Properties: - - `source$`: Source observable that the Component will subscribe to. - - `graceTime`: an optional property that describes the amount of time in ms - that the Component should wait before unsubscribing from the source observable - after it unmounts (default = 200). - -Important: This Component doesn't trigger any updates. - ## Examples - [This is a contrived example](https://codesandbox.io/s/crazy-wood-vn7gg?file=/src/fakeApi.js) based on [this example](https://reactjs.org/docs/concurrent-mode-patterns.html#reviewing-the-changes) from the React docs. diff --git a/packages/core/README.md b/packages/core/README.md new file mode 100644 index 0000000..880e840 --- /dev/null +++ b/packages/core/README.md @@ -0,0 +1,127 @@ +# @react-rxjs/core + +## Installation + npm install @react-rxjs/core + +## API + +### connectObservable +```ts +const [useCounter, sharedCounter$] = connectObservable( + clicks$.pipe( + scan(prev => prev + 1, 0), + startWith(0), + ) +) +``` +Accepts: An Observable. + +Returns `[1, 2]` + +1. A React Hook that yields the latest emitted value of the observable. If the +Observable doesn't synchronously emit a value upon the first subscription, then +the hook will leverage React Suspense while it's waiting for the first value. + +2. A `sharedLatest` version of the observable. It can be used for composing other +streams that depend on it. The shared subscription is closed as soon as there +are no subscribers to that observable. + +### connectFactoryObservable +```tsx +const [useStory, getStory$] = connectFactoryObservable( + (storyId: number) => getStoryWithUpdates$(storyId) +) + +const Story: React.FC<{id: number}> = ({id}) => { + const story = useStory(id); + + return ( +
+

{story.title}

+

{story.description}

+
+ ) +} +``` +Accepts: A factory function that returns an Observable. + +Returns `[1, 2]` + +1. A React Hook function with the same parameters as the factory function. This hook +will yield the latest update from the observable returned from the factory function. +If the Observable doesn't synchronously emit a value upon the first subscription, then +the hook will leverage React Suspense while it's waiting for the first value. + +2. A `sharedLatest` version of the observable returned by the factory function. It +can be used for composing other streams that depend on it. The shared subscription +is closed as soon as there are no subscribers to that observable. + +### shareLatest +```ts +const activePlanetName$ = planet$.pipe( + filter(planet => planet.isActive), + map(planet => planet.name), + shareLatest() +) +``` + +A RxJS pipeable operator which shares and replays the latest emitted value. It's +the equivalent of: + +```ts +const shareLatest = (): Observable => + source$.pipe( + multicast(() => new ReplaySubject(1)), + refCount(), + ) +``` + +The enhanced observables returned from `connectObservable` and `connectFactoryObservable` +have been enhanced with this operator. + +### SUSPENSE + +```ts +const story$ = selectedStoryId$.pipe( + switchMap(id => concat( + SUSPENSE, + getStory$(id) + )) +) +``` + +This is a special symbol that can be emitted from our observables to let the react hook +know that there is a value on its way, and that we want to leverage React Suspense +while we are waiting for that value. + +### suspend + +```ts +const story$ = selectedStoryId$.pipe( + switchMap(id => suspend(getStory$(id)) +) +``` + +A RxJS creation operator that prepends a `SUSPENSE` on the source observable. + +### suspended + +```ts +const story$ = selectedStoryId$.pipe( + switchMap(id => getStory$(id).pipe( + suspended() + )) +) +``` + +The pipeable version of `suspend` + +### switchMapSuspended + +```ts +const story$ = selectedStoryId$.pipe( + switchMapSuspended(getStory$) +) +``` + +Like `switchMap` but applying a `startWith(SUSPENSE)` to the inner observable. diff --git a/packages/dom/README.md b/packages/dom/README.md new file mode 100644 index 0000000..3693db6 --- /dev/null +++ b/packages/dom/README.md @@ -0,0 +1,24 @@ +# @react-rxjs/dom + +## Installation + npm install @react-rxjs/dom + +## API + +### batchUpdates + +A RxJS pipeable operator which observes the source observable on +an asapScheduler and uses `ReactDom.unstable_batchedUpdates` to emit the +values. It's useful for observing streams of events that come from outside +of ReactDom event-handlers. + +IMPORTANT: This operator will be deprecated when React 17 is released +(or whenever React CM is released). The reason being that React Concurrent Mode +automatically batches all synchronous updates. Meaning that with React CM, +observing a stream through the asapScheduler accomplishes the same thing. + +```ts +const marketUpdates$ = defer(() => api.getMarketUpdates()).pipe( + batchUpdates() +) +``` diff --git a/packages/dom/src/batchUpdates.ts b/packages/dom/src/batchUpdates.ts index cee7fa0..cf354bb 100644 --- a/packages/dom/src/batchUpdates.ts +++ b/packages/dom/src/batchUpdates.ts @@ -3,13 +3,13 @@ import { unstable_batchedUpdates } from "react-dom" /** * A RxJS pipeable operator which observes the source observable on - * an asapScheduler and uses `ReactDom.unstable_batchedUpdates` to emmit the + * an asapScheduler and uses `ReactDom.unstable_batchedUpdates` to emit the * values. It's useful for observing streams of events that come from outside * of ReactDom event-handlers. * * @remarks This operator will be deprecated when React 17 is released - * (or whnever React CM is released). The reason being that React Concurrent Mode - * automatically batches all synchrous updates. Meaning that with React CM, + * (or whenever React CM is released). The reason being that React Concurrent Mode + * automatically batches all synchronous updates. Meaning that with React CM, * observing a stream through the asapScheduler accomplishes the same thing. */ export const batchUpdates = () => ( diff --git a/packages/utils/README.md b/packages/utils/README.md new file mode 100644 index 0000000..3c4194b --- /dev/null +++ b/packages/utils/README.md @@ -0,0 +1,119 @@ +# @react-rxjs/utils + +## Installation + npm install @react-rxjs/utils + +## API + +### useSubscribe + +A React hook that creates a subscription to the provided observable once the +component mounts and it unsubscribes when the component unmounts. + +Arguments: + - `source$`: Source observable that the hook will subscribe to. + - `unsubscribeGraceTime`: Amount of time in ms that the hook should wait before + unsubscribing from the source observable after it unmounts (default = 200). + +Important: This hook doesn't trigger any updates. + +### Subscribe + +A React Component that creates a subscription to the provided observable once +the component mounts and it unsubscribes from it when the component unmounts. + +Properties: + - `source$`: Source observable that the Component will subscribe to. + - `graceTime`: an optional property that describes the amount of time in ms + that the Component should wait before unsubscribing from the source observable + after it unmounts (default = 200). + +Important: This Component doesn't trigger any updates. + +### groupInMap + +A RxJS pipeable operator which groups all values by key and emits a Map that +holds the latest value for each key + +Arguments: + - `keyGetter`: A function that extracts the key for each item. + - `projection`: Projection function for each group. + +```ts +const votesByKey$ = new Subject<{key: string}>() +const counters$ = votesByKey$.pipe( + groupInMap( + vote => vote.key, + votes$ => votes$.pipe( + mapTo(1), + scan(count => count + 1), + takeWhile(count => count < 3) + ) + ) +) + +counters$.subscribe(counters => { + console.log('counters$:') + counters.forEach((value, key) => { + console.log(`${key}: ${value}`); + }) +}) + +votesByKey$.next({key: 'foo'}) +// > counters$: +// > foo: 1 + +votesByKey$.next({key: 'foo'}) +// > counters$: +// > foo: 2 + +votesByKey$.next({key: 'bar'}) +// > counters$: +// > foo: 2 +// > bar: 1 + +votesByKey$.next({key: 'foo'}) +// > counters$: +// > bar: 1 + +votesByKey$.next({key: 'bar'}) +// > counters$: +// > bar: 2 +// +votesByKey$.next({key: 'bar'}) +// > counters$: +``` + +### mergeWithKey + +Emits the values from all the streams of the provided object, in a result +which provides the key of the stream of that emission. + +Arguments: + - `inputObject`: Object of streams + +```ts +const inc$ = new Subject() +const dec$ = new Subject() +const resetTo$ = new Subject() + +const counter$ = mergeWithKey({ + inc$, + dec$, + resetTo$, +}).pipe( + scan((acc, current) => { + switch (current.type) { + case "inc$": + return acc + 1 + case "dec$": + return acc - 1 + case "resetTo$": + return current.payload + default: + return acc + } + }, 0), + startWith(0), +) +``` diff --git a/packages/utils/src/groupInMap.ts b/packages/utils/src/groupInMap.ts index 0975345..7b9bbfd 100644 --- a/packages/utils/src/groupInMap.ts +++ b/packages/utils/src/groupInMap.ts @@ -12,10 +12,10 @@ import continuousGroupBy from "./continuousGroupBy" const DELETE = Symbol("DELETE") /** - * Groups all values by key and emits a Map that hold the latest value for each + * Groups all values by key and emits a Map that holds the latest value for each * key. * - * @param keyGetter Key getter. + * @param keyGetter A function that extracts the key for each item. * @param projection Projection function for each group. */ export const groupInMap = ( diff --git a/packages/utils/src/mergeWithKey.ts b/packages/utils/src/mergeWithKey.ts index e3d6133..ecff7ae 100644 --- a/packages/utils/src/mergeWithKey.ts +++ b/packages/utils/src/mergeWithKey.ts @@ -4,7 +4,7 @@ import { map } from "rxjs/operators" /** * Emits the values from all the streams of the provided object, in a result * which provides the key of the stream of that emission. - * + * * @param input object of streams */ export const mergeWithKey: < @@ -23,7 +23,9 @@ export const mergeWithKey: < ...(Object.entries(input) .map( ([type, stream]) => - from(stream).pipe(map(payload => ({ type, payload } as any))) as any, + from(stream).pipe( + map((payload) => ({ type, payload } as any)), + ) as any, ) .concat(optionalArgs) as any[]), )