mirror of
https://github.com/re-rxjs/react-rxjs.git
synced 2025-12-08 18:01:51 +00:00
chore(core-utils): move Suspense utils from core to utils
This commit is contained in:
parent
047f83d0a2
commit
0d40dca5c3
102
README.md
102
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 (
|
||||
<article>
|
||||
@ -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<Repo[]> =>
|
||||
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<Repo[]> =>
|
||||
)
|
||||
|
||||
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 (
|
||||
<ul>
|
||||
{repos.map(repo => (
|
||||
{repos.map((repo) => (
|
||||
<li key={repo.id}>
|
||||
<Repo {...repo} />
|
||||
</li>
|
||||
@ -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
|
||||
|
||||
<!-- markdownlint-enable -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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 = () =>
|
||||
@ -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.
|
||||
@ -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.
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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) => {
|
||||
@ -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.
|
||||
@ -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) => {
|
||||
@ -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) => {
|
||||
Loading…
x
Reference in New Issue
Block a user