[](https://travis-ci.org/re-rxjs/re-rxjs)
[](https://codecov.io/gh/re-rxjs/re-rxjs)
[](https://www.npmjs.com/package/re-rxjs)
[](https://github.com/re-rxjs/re-rxjs/blob/main/LICENSE)
[](#contributors-)
[](http://makeapullrequest.com)
[](https://github.com/re-rxjs/re-rxjs/blob/main/CODE_OF_CONDUCT.md)
Main features
-------------
- :cyclone: Truly Reactive
- :zap: Highly performant and free of memory-leaks
- :twisted_rightwards_arrows: First class support for React Suspense and [ready for Concurrent Mode](https://github.com/dai-shi/will-this-react-global-state-work-in-concurrent-mode#results)
- :scissors: Decentralized and composable, thus enabling optimal code-splitting
- :microscope: [Tiny and tree-shakeable](https://bundlephobia.com/result?p=re-rxjs)
- :muscle: Supports TypeScript
## Table of Contents
- [Installation](#installation)
- [API](#api)
- [connectObservable](#connectObservable)
- [connectFactoryObservable](#connectFactoryObservable)
- [distinctShareReplay](#distinctShareReplay)
- [SUSPENSE](#SUSPENSE)
- [suspend](#suspend)
- [suspended](#suspended)
- [switchMapSuspended](#switchMapSuspended)
- [createInput](#createInput)
- [Examples](#examples)
## Installation
npm install re-rxjs
## API
### connectObservable
```ts
const [useCounter, sharedCounter$] = connectObservable(
clicks$.pipe(
scan(prev => prev + 1, 0),
startWith(0),
)
)
```
Returns a hook that provides the latest update of the accepted observable, and the
underlying enhanced observable, which shares the subscription to all of its subscribers,
and always emits the latest value when subscribing to 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.
2. A shared replayable version of the observable generated by the factory function that
can be used for composing other streams that depend on it. This observable is disposed
when its `refCount` goes down to zero.
### distinctShareReplay
```ts
const activePlanetName$ = planet$.pipe(
filter(planet => planet.isActive),
map(planet => planet.name),
distinctShareReplay()
)
```
A RxJS pipeable operator which performs a custom `shareReplay` that can be useful
when working with these bindings. It's roughly the equivalent of:
```ts
const distinctShareReplay = (compare?: Object.is) => (
source$: Observable,
): Observable =>
source$.pipe(
distinctUntilChanged(compare),
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 API
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.
### createInput
A couple examples are worth a thousand words:
```tsx
const [getCounter$, setCounter] = createInput(0)
const useCounter = connectFactoryObservable((id: string) => getCounter$(id))
const Counter: React.FC<{id: string}> = ({id}) => {
const counter = useCounter(id);
return (
{counter}
merge(
getUpClicks$(id).pipe(mapTo(1)),
getDownClicks$(id).pipe(mapTo(-1)),
).pipe(
scan((a, b) => a + b, 0),
startWith(0)
)
)
const Counter: React.FC<{id: string}> = ({id}) => {
const counter = useCounter(id);
return (
-
{counter}
+()
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 =>
(rawData.items ?? []).map((repo: any) => ({
id: repo.id,
name: repo.name,
description: repo.description,
author: repo.owner.login,
stars: repo.stargazers_count,
lastUpdate: Date.parse(repo.update_at),
})),
)
const [useRepos, repos$] = connectObservable(
searchInput$.pipe(
switchMapSuspended(findRepos),
startWith(null),
),
)
function Repos() {
const repos = useRepos()
if (repos === null) {
return null
}
if (repos.length === 0) {
return