From 0d40dca5c3aa81a656d49d25d030d58b7c1a3636 Mon Sep 17 00:00:00 2001 From: Josep M Sobrepere Date: Wed, 26 Aug 2020 22:06:38 +0200 Subject: [PATCH] chore(core-utils): move Suspense utils from core to utils --- README.md | 102 ++++++------------ packages/core/README.md | 28 ----- packages/core/src/index.tsx | 8 +- .../src/{operators => }/share-latest.test.ts | 2 +- .../core/src/{operators => }/shareLatest.ts | 4 +- packages/utils/README.md | 28 +++++ packages/utils/src/index.tsx | 3 + .../operators => utils/src}/suspend.test.ts | 3 +- .../src/operators => utils/src}/suspend.ts | 2 +- .../operators => utils/src}/suspended.test.ts | 3 +- .../src/operators => utils/src}/suspended.ts | 0 .../src}/switchMapSuspended.test.ts | 3 +- .../src}/switchMapSuspended.ts | 0 13 files changed, 76 insertions(+), 110 deletions(-) rename packages/core/src/{operators => }/share-latest.test.ts (98%) rename packages/core/src/{operators => }/shareLatest.ts (80%) rename packages/{core/src/operators => utils/src}/suspend.test.ts (87%) rename packages/{core/src/operators => utils/src}/suspend.ts (87%) rename packages/{core/src/operators => utils/src}/suspended.test.ts (87%) rename packages/{core/src/operators => utils/src}/suspended.ts (100%) rename packages/{core/src/operators => utils/src}/switchMapSuspended.test.ts (92%) rename packages/{core/src/operators => utils/src}/switchMapSuspended.ts (100%) diff --git a/README.md b/README.md index 310b0c1..6100cdd 100644 --- a/README.md +++ b/README.md @@ -28,15 +28,11 @@ - [Observable overload](#observable-overload) - [Factory of Observables overload](#factory-of-observables-overload) - [shareLatest](#sharelatest) - - React Suspense Support - [SUSPENSE](#suspense) - - [suspend](#suspend) - - [suspended](#suspended) - - [switchMapSuspended](#switchmapsuspended) - [Examples](#examples) - ## Installation + npm install @react-rxjs/core ## API @@ -44,34 +40,37 @@ ### bind #### Observable overload + ```ts const [useCounter, sharedCounter$] = bind( clicks$.pipe( - scan(prev => prev + 1, 0), + 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. + 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. + streams that depend on it. The shared subscription is closed as soon as there + are no subscribers to that observable. #### Factory of Observables overload + ```tsx -const [useStory, getStory$] = bind( - (storyId: number) => getStoryWithUpdates$(storyId) +const [useStory, getStory$] = bind((storyId: number) => + getStoryWithUpdates$(storyId), ) -const Story: React.FC<{id: number}> = ({id}) => { - const story = useStory(id); +const Story: React.FC<{ id: number }> = ({ id }) => { + const story = useStory(id) return (
@@ -81,25 +80,27 @@ const Story: React.FC<{id: number}> = ({id}) => { ) } ``` + 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. + 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. + 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() + filter((planet) => planet.isActive), + map((planet) => planet.name), + shareLatest(), ) ``` @@ -120,10 +121,7 @@ The enhanced observables returned from `bind` have been enhanced with this opera ```ts const story$ = selectedStoryId$.pipe( - switchMap(id => concat( - SUSPENSE, - getStory$(id) - )) + switchMap((id) => concat(SUSPENSE, getStory$(id))), ) ``` @@ -131,39 +129,8 @@ This is a special symbol that can be emitted from our observables to let the rea 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. - ## 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. - A search for Github repos that highlights the most recently updated one: @@ -172,7 +139,8 @@ Like `switchMap` but applying a `startWith(SUSPENSE)` to the inner observable. import React, { Suspense } from "react" import { Subject } from "rxjs" import { startWith, map } from "rxjs/operators" -import { bind, switchMapSuspended } from "@react-rxjs/core" +import { bind } from "@react-rxjs/core" +import { switchMapSuspended } from "@react-rxjs/utils" import { Header, Search, LoadingResults, Repo } from "./components" interface Repo { @@ -189,8 +157,8 @@ const onSubmit = (value: string) => searchInput$.next(value) const findRepos = (query: string): Promise => fetch(`https://api.github.com/search/repositories?q=${query}`) - .then(response => response.json()) - .then(rawData => + .then((response) => response.json()) + .then((rawData) => (rawData.items ?? []).map((repo: any) => ({ id: repo.id, name: repo.name, @@ -202,10 +170,7 @@ const findRepos = (query: string): Promise => ) const [useRepos, repos$] = bind( - searchInput$.pipe( - switchMapSuspended(findRepos), - startWith(null), - ), + searchInput$.pipe(switchMapSuspended(findRepos), startWith(null)), ) function Repos() { @@ -221,7 +186,7 @@ function Repos() { return (
    - {repos.map(repo => ( + {repos.map((repo) => (
  • @@ -232,7 +197,7 @@ function Repos() { const [useMostRecentlyUpdatedRepo] = bind( repos$.pipe( - map(repos => + map((repos) => Array.isArray(repos) && repos.length > 0 ? repos.reduce((winner, current) => current.lastUpdate > winner.lastUpdate ? current : winner, @@ -292,6 +257,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d + This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! diff --git a/packages/core/README.md b/packages/core/README.md index 0d21e1a..f28dd89 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -97,31 +97,3 @@ const story$ = selectedStoryId$.pipe( 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/core/src/index.tsx b/packages/core/src/index.tsx index 3318845..df8b054 100644 --- a/packages/core/src/index.tsx +++ b/packages/core/src/index.tsx @@ -1,9 +1,3 @@ -// core export { bind } from "./bind" -export { shareLatest } from "./operators/shareLatest" - -// support for React Suspense +export { shareLatest } from "./shareLatest" export { SUSPENSE } from "./SUSPENSE" -export { suspend } from "./operators/suspend" -export { suspended } from "./operators/suspended" -export { switchMapSuspended } from "./operators/switchMapSuspended" diff --git a/packages/core/src/operators/share-latest.test.ts b/packages/core/src/share-latest.test.ts similarity index 98% rename from packages/core/src/operators/share-latest.test.ts rename to packages/core/src/share-latest.test.ts index 5597e10..256e454 100644 --- a/packages/core/src/operators/share-latest.test.ts +++ b/packages/core/src/share-latest.test.ts @@ -1,6 +1,6 @@ import { TestScheduler } from "rxjs/testing" import { from, merge, defer } from "rxjs" -import { shareLatest } from "../" +import { shareLatest } from "./" import { withLatestFrom, startWith, map } from "rxjs/operators" const scheduler = () => diff --git a/packages/core/src/operators/shareLatest.ts b/packages/core/src/shareLatest.ts similarity index 80% rename from packages/core/src/operators/shareLatest.ts rename to packages/core/src/shareLatest.ts index caf7055..77ee34b 100644 --- a/packages/core/src/operators/shareLatest.ts +++ b/packages/core/src/shareLatest.ts @@ -1,6 +1,6 @@ import { Observable } from "rxjs" -import internalShareLatest from "../internal/share-latest" -import { takeUntilComplete } from "../internal/take-until-complete" +import internalShareLatest from "./internal/share-latest" +import { takeUntilComplete } from "./internal/take-until-complete" /** * A RxJS pipeable operator which shares and replays the latest emitted value. diff --git a/packages/utils/README.md b/packages/utils/README.md index 1de1281..daa8aff 100644 --- a/packages/utils/README.md +++ b/packages/utils/README.md @@ -146,3 +146,31 @@ const counter$ = mergeWithKey({ startWith(0), ) ``` + +### 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/utils/src/index.tsx b/packages/utils/src/index.tsx index 550e8f5..41e5fd4 100644 --- a/packages/utils/src/index.tsx +++ b/packages/utils/src/index.tsx @@ -4,3 +4,6 @@ export { collectValues } from "./collectValues" export { collect } from "./collect" export { mergeWithKey } from "./mergeWithKey" export { split } from "./split" +export { suspend } from "./suspend" +export { suspended } from "./suspended" +export { switchMapSuspended } from "./switchMapSuspended" diff --git a/packages/core/src/operators/suspend.test.ts b/packages/utils/src/suspend.test.ts similarity index 87% rename from packages/core/src/operators/suspend.test.ts rename to packages/utils/src/suspend.test.ts index 1f0567e..837c973 100644 --- a/packages/core/src/operators/suspend.test.ts +++ b/packages/utils/src/suspend.test.ts @@ -1,5 +1,6 @@ import { TestScheduler } from "rxjs/testing" -import { suspend, SUSPENSE } from "../" +import { SUSPENSE } from "@react-rxjs/core" +import { suspend } from "./" const scheduler = () => new TestScheduler((actual, expected) => { diff --git a/packages/core/src/operators/suspend.ts b/packages/utils/src/suspend.ts similarity index 87% rename from packages/core/src/operators/suspend.ts rename to packages/utils/src/suspend.ts index 9841af0..7a2a74c 100644 --- a/packages/core/src/operators/suspend.ts +++ b/packages/utils/src/suspend.ts @@ -1,6 +1,6 @@ import { ObservableInput, from } from "rxjs" import { startWith } from "rxjs/operators" -import { SUSPENSE } from "../SUSPENSE" +import { SUSPENSE } from "@react-rxjs/core" /** * A RxJS creation operator that prepends a SUSPENSE on the source observable. diff --git a/packages/core/src/operators/suspended.test.ts b/packages/utils/src/suspended.test.ts similarity index 87% rename from packages/core/src/operators/suspended.test.ts rename to packages/utils/src/suspended.test.ts index 8c5f7cd..5abf458 100644 --- a/packages/core/src/operators/suspended.test.ts +++ b/packages/utils/src/suspended.test.ts @@ -1,5 +1,6 @@ import { TestScheduler } from "rxjs/testing" -import { suspended, SUSPENSE } from "../" +import { SUSPENSE } from "@react-rxjs/core" +import { suspended } from "./" const scheduler = () => new TestScheduler((actual, expected) => { diff --git a/packages/core/src/operators/suspended.ts b/packages/utils/src/suspended.ts similarity index 100% rename from packages/core/src/operators/suspended.ts rename to packages/utils/src/suspended.ts diff --git a/packages/core/src/operators/switchMapSuspended.test.ts b/packages/utils/src/switchMapSuspended.test.ts similarity index 92% rename from packages/core/src/operators/switchMapSuspended.test.ts rename to packages/utils/src/switchMapSuspended.test.ts index 812c1a1..045d162 100644 --- a/packages/core/src/operators/switchMapSuspended.test.ts +++ b/packages/utils/src/switchMapSuspended.test.ts @@ -1,5 +1,6 @@ import { TestScheduler } from "rxjs/testing" -import { switchMapSuspended, SUSPENSE } from "../" +import { SUSPENSE } from "@react-rxjs/core" +import { switchMapSuspended } from "./" const scheduler = () => new TestScheduler((actual, expected) => { diff --git a/packages/core/src/operators/switchMapSuspended.ts b/packages/utils/src/switchMapSuspended.ts similarity index 100% rename from packages/core/src/operators/switchMapSuspended.ts rename to packages/utils/src/switchMapSuspended.ts