{"id":13441202,"url":"https://github.com/LeetCode-OpenSource/rxjs-hooks","last_synced_at":"2025-03-20T11:37:32.293Z","repository":{"id":37270322,"uuid":"157676260","full_name":"LeetCode-OpenSource/rxjs-hooks","owner":"LeetCode-OpenSource","description":"React hooks for RxJS","archived":false,"fork":false,"pushed_at":"2023-04-07T09:44:29.000Z","size":5167,"stargazers_count":2182,"open_issues_count":20,"forks_count":83,"subscribers_count":28,"default_branch":"master","last_synced_at":"2024-10-24T01:32:11.475Z","etag":null,"topics":["observable","react","react-hooks","rxjs","rxjs6"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LeetCode-OpenSource.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-11-15T08:18:54.000Z","updated_at":"2024-09-21T02:41:18.000Z","dependencies_parsed_at":"2024-01-12T02:14:10.219Z","dependency_job_id":null,"html_url":"https://github.com/LeetCode-OpenSource/rxjs-hooks","commit_stats":{"total_commits":1072,"total_committers":18,"mean_commits":59.55555555555556,"dds":0.5708955223880596,"last_synced_commit":"ca00ae52073f1d1eee0895d7921ca86bb5c5b365"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeetCode-OpenSource%2Frxjs-hooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeetCode-OpenSource%2Frxjs-hooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeetCode-OpenSource%2Frxjs-hooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeetCode-OpenSource%2Frxjs-hooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LeetCode-OpenSource","download_url":"https://codeload.github.com/LeetCode-OpenSource/rxjs-hooks/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221759943,"owners_count":16876322,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["observable","react","react-hooks","rxjs","rxjs6"],"created_at":"2024-07-31T03:01:31.086Z","updated_at":"2024-10-28T01:30:17.252Z","avatar_url":"https://github.com/LeetCode-OpenSource.png","language":"TypeScript","readme":"# React hooks for RxJS\n[![CircleCI](https://circleci.com/gh/LeetCode-OpenSource/rxjs-hooks.svg?style=svg)](https://circleci.com/gh/LeetCode-OpenSource/rxjs-hooks)\n[![codecov](https://codecov.io/gh/LeetCode-OpenSource/rxjs-hooks/branch/master/graph/badge.svg)](https://codecov.io/gh/LeetCode-OpenSource/rxjs-hooks)\n[![npm version](https://badge.fury.io/js/rxjs-hooks.svg)](https://badge.fury.io/js/rxjs-hooks)\n\n- [Installation](#installation)\n- [Demo](#quick-look)\n- [Apis](#apis)\n  1. [useObservable](#useobservable)\n  2. [useEventCallback](#useeventcallback)\n\n## Installation\n\nUsing npm:\n\n```\n$ npm i --save rxjs-hooks rxjs\n```\n\nOr yarn:\n\n```\n$ yarn add rxjs-hooks rxjs\n```\n\n## Quick look\n\n- [useObservable - live demo](https://codesandbox.io/s/rxjs-hooks-basic-oee81n)\n\n```javascript\nimport React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport { useObservable } from \"rxjs-hooks\";\nimport { interval } from \"rxjs\";\nimport { map } from \"rxjs/operators\";\n\nfunction App() {\n  const value = useObservable(() =\u003e interval(500).pipe(map((val) =\u003e val * 3)));\n\n  return (\n    \u003cdiv className=\"App\"\u003e\n      \u003ch1\u003eIncremental number: {value}\u003c/h1\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n- [useEventCallback - live demo](https://codesandbox.io/s/rxjs-hooks-useeventcallback-forked-5s1mvq)\n\n```javascript\nimport React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport { useEventCallback } from \"rxjs-hooks\";\nimport { map } from \"rxjs/operators\";\n\nfunction App() {\n  const [clickCallback, [description, x, y]] = useEventCallback((event$) =\u003e\n    event$.pipe(\n      map((event) =\u003e [event.target.innerHTML, event.clientX, event.clientY]),\n    ),\n    [\"nothing\", 0, 0],\n  )\n\n  return (\n    \u003cdiv className=\"App\"\u003e\n      \u003ch1\u003eclick position: {x}, {y}\u003c/h1\u003e\n      \u003ch1\u003e\"{description}\" was clicked.\u003c/h1\u003e\n      \u003cbutton onClick={clickCallback}\u003eclick me\u003c/button\u003e\n      \u003cbutton onClick={clickCallback}\u003eclick you\u003c/button\u003e\n      \u003cbutton onClick={clickCallback}\u003eclick him\u003c/button\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n## Apis\n\n### `useObservable`\n\n```tsx\nexport type InputFactory\u003cState\u003e = (state$: Observable\u003cState\u003e) =\u003e Observable\u003cState\u003e\nexport type InputFactoryWithInputs\u003cState, Inputs\u003e = (\n  state$: Observable\u003cState\u003e,\n  inputs$: Observable\u003cRestrictArray\u003cInputs\u003e\u003e,\n) =\u003e Observable\u003cState\u003e\n\nexport function useObservable\u003cState\u003e(inputFactory: InputFactory\u003cState\u003e): State | null\nexport function useObservable\u003cState\u003e(inputFactory: InputFactory\u003cState\u003e, initialState: State): State\nexport function useObservable\u003cState, Inputs\u003e(\n  inputFactory: InputFactoryWithInputs\u003cState, Inputs\u003e,\n  initialState: State,\n  inputs: RestrictArray\u003cInputs\u003e,\n): State\n```\n\n#### Examples:\n\n```tsx\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport { useObservable } from 'rxjs-hooks'\nimport { of } from 'rxjs'\n\nfunction App() {\n  const value = useObservable(() =\u003e of(1000))\n  return (\n    // render twice\n    // null and 1000\n    \u003ch1\u003e{value}\u003c/h1\u003e\n  )\n}\n```\n\n**With default value:**\n\n```tsx\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport { useObservable } from 'rxjs-hooks'\nimport { of } from 'rxjs'\n\nfunction App() {\n  const value = useObservable(() =\u003e of(1000), 200)\n  return (\n    // render twice\n    // 200 and 1000\n    \u003ch1\u003e{value}\u003c/h1\u003e\n  )\n}\n```\n\n**Observe props change:**\n```tsx\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport { useObservable } from 'rxjs-hooks'\nimport { map } from 'rxjs/operators'\n\nfunction App(props: { foo: number }) {\n  const value = useObservable((_, inputs$) =\u003e inputs$.pipe(\n    map(([val]) =\u003e val + 1),\n  ), 200, [props.foo])\n  return (\n    // render three times\n    // 200 and 1001 and 2001\n    \u003ch1\u003e{value}\u003c/h1\u003e\n  )\n}\nconst rootElement = document.querySelector(\"#app\");\nReactDOM.createRoot(rootElement).render(\u003cApp foo={1000}/\u003e);\nReactDOM.createRoot(rootElement).render(\u003cApp foo={2000}/\u003e);\n```\n\n**useObservable with state$**\n\n[live demo](https://codesandbox.io/s/rxjs-hooks-useobservable-with-state-forked-9fjefy)\n\n```tsx\nimport React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport { useObservable } from 'rxjs-hooks'\nimport { interval } from 'rxjs'\nimport { map, withLatestFrom } from 'rxjs/operators'\n\nfunction App() {\n  const value = useObservable((state$) =\u003e interval(1000).pipe(\n    withLatestFrom(state$),\n    map(([_num, state]) =\u003e state * state),\n  ), 2)\n  return (\n    // 2\n    // 4\n    // 16\n    // 256\n    // ...\n    \u003ch1\u003e{value}\u003c/h1\u003e\n  )\n}\n```\n\n### `useEventCallback`\n\n#### Examples:\n\n```tsx\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport { useEventCallback } from 'rxjs-hooks'\nimport { mapTo } from 'rxjs/operators'\n\nfunction App() {\n  const [clickCallback, value] = useEventCallback((event$: Observable\u003cReact.SyntheticEvent\u003cHTMLButtonElement\u003e\u003e) =\u003e\n    event$.pipe(\n      mapTo(1000)\n    )\n  )\n  return (\n    // render null\n    // click button\n    // render 1000\n    \u003c\u003e\n      \u003ch1\u003e{value}\u003c/h1\u003e\n      \u003cbutton onClick={clickCallback}\u003eclick me\u003c/button\u003e\n    \u003c/\u003e\n  )\n}\n\n```\n\n**With initial value:**\n\n```tsx\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport { useEventCallback } from 'rxjs-hooks'\nimport { mapTo } from 'rxjs/operators'\n\nfunction App() {\n  const [clickCallback, value] = useEventCallback((event$: Observable\u003cReact.SyntheticEvent\u003cHTMLButtonElement\u003e\u003e) =\u003e\n    event$.pipe(\n      mapTo(1000)\n    ),\n    200,\n  )\n  return (\n    // render 200\n    // click button\n    // render 1000\n    \u003c\u003e\n      \u003ch1\u003e{value}\u003c/h1\u003e\n      \u003cbutton onClick={clickCallback}\u003eclick me\u003c/button\u003e\n    \u003c/\u003e\n  )\n}\n\n```\n\n**With state$:**\n\n[live demo](https://codesandbox.io/s/rxjs-hooks-useeventcallback-with-state-forked-rg1frx)\n\n```tsx\nimport React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport { useEventCallback } from \"rxjs-hooks\";\nimport { map, withLatestFrom } from \"rxjs/operators\";\n\nfunction App() {\n  const [clickCallback, [description, x, y, prevDescription]] = useEventCallback(\n    (event$, state$) =\u003e\n      event$.pipe(\n        withLatestFrom(state$),\n        map(([event, state]) =\u003e [\n           event.target.innerHTML,\n           event.clientX,\n           event.clientY,\n          state[0],\n        ])\n      ),\n    [\"nothing\", 0, 0, \"nothing\"]\n  );\n\n  return (\n    \u003cdiv className=\"App\"\u003e\n      \u003ch1\u003e\n        click position: {x}, {y}\n      \u003c/h1\u003e\n      \u003ch1\u003e\"{description}\" was clicked.\u003c/h1\u003e\n      \u003ch1\u003e\"{prevDescription}\" was clicked previously.\u003c/h1\u003e\n      \u003cbutton onClick={clickCallback}\u003eclick me\u003c/button\u003e\n      \u003cbutton onClick={clickCallback}\u003eclick you\u003c/button\u003e\n      \u003cbutton onClick={clickCallback}\u003eclick him\u003c/button\u003e\n    \u003c/div\u003e\n  );\n}\n\n```\n\n**A complex example: useEventCallback with both `inputs$` and `state$`**\n\n[live demo](https://codesandbox.io/s/rxjs-hooks-useeventcallback-with-state-forked-tlwdil)\n\n```tsx\nimport React, { useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport { useEventCallback } from \"rxjs-hooks\";\nimport { map, withLatestFrom, combineLatest } from \"rxjs/operators\";\n\nimport \"./styles.css\";\n\nfunction App() {\n  const [count, setCount] = useState(0);\n  const [clickCallback, [description, x, y, prevDesc]] = useEventCallback(\n    (event$, state$, inputs$) =\u003e\n      event$.pipe(\n        map(event =\u003e [event.target.innerHTML, event.clientX, event.clientY]),\n        combineLatest(inputs$),\n        withLatestFrom(state$),\n        map(([eventAndInput, state]) =\u003e {\n          const [[text, x, y], [count]] = eventAndInput;\n          const prevDescription = state[0];\n          return [text, x + count, y + count, prevDescription];\n        })\n      ),\n    [\"nothing\", 0, 0, \"nothing\"],\n    [count]\n  );\n\n  return (\n    \u003cdiv className=\"App\"\u003e\n      \u003ch1\u003e\n        click position: {x}, {y}\n      \u003c/h1\u003e\n      \u003ch1\u003e\"{description}\" was clicked.\u003c/h1\u003e\n      \u003ch1\u003e\"{prevDesc}\" was clicked previously.\u003c/h1\u003e\n      \u003cbutton onClick={clickCallback}\u003eclick me\u003c/button\u003e\n      \u003cbutton onClick={clickCallback}\u003eclick you\u003c/button\u003e\n      \u003cbutton onClick={clickCallback}\u003eclick him\u003c/button\u003e\n      \u003cdiv\u003e\n        \u003cp\u003e\n          click buttons above, and then click this `+++` button, the position\n          numbers will grow.\n        \u003c/p\u003e\n        \u003cbutton onClick={() =\u003e setCount(count + 1)}\u003e+++\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n}\n\n```\n\n**Example of combining callback observables coming from separate elements - animation with start/stop button and rate controllable via slider**\n\n[live demo](https://codesandbox.io/s/late-wood-x5rg0l)\n\n```tsx\nconst Animation = ({ frame }) =\u003e {\n  const frames = \"|/-\\\\|/-\\\\|\".split(\"\");\n  return (\n    \u003cdiv\u003e\n      \u003cp\u003e{frames[frame % frames.length]}\u003c/p\u003e\n    \u003c/div\u003e\n  );\n};\n\n\nconst App = () =\u003e {\n  const defaultRate = 5;\n\n  const [running, setRunning] = useState(false);\n\n  const [onEvent, frame] = useEventCallback(events$ =\u003e {\n    const running$ = events$.pipe(\n      filter(e =\u003e e.type === \"click\"),\n      scan(running =\u003e !running, running),\n      startWith(running),\n      tap(setRunning)\n    );\n\n    return events$.pipe(\n      filter(e =\u003e e.type === \"change\"),\n      map(e =\u003e parseInt(e.target.value, 10)),\n      startWith(defaultRate),\n      switchMap(i =\u003e timer(200, 1000 / i)),\n      withLatestFrom(running$),\n      filter(([_, running]) =\u003e running),\n      scan(frame =\u003e frame + 1, 0)\n    );\n  });\n\n  return (\n    \u003cdiv className=\"App\"\u003e\n      \u003cbutton onClick={onEvent}\u003e{running ? \"Stop\" : \"Start\"}\u003c/button\u003e\n      \u003cinput\n        type=\"range\"\n        onChange={onEvent}\n        defaultValue={defaultRate}\n        min=\"1\"\n        max=\"10\"\n      \u003e\u003c/input\u003e\n      \u003cAnimation frame={frame} /\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n## Known issues\nIf you are using React 18 + `StrictMode` under NODE_ENV=development, `rxjs-hooks` may not work properly. https://github.com/facebook/react/issues/24502#issuecomment-1118846544\n","funding_links":[],"categories":["HarmonyOS","Packages","TypeScript","React","React \u0026 RxJS"],"sub_categories":["Windows Manager","Framework-Agnostic"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLeetCode-OpenSource%2Frxjs-hooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FLeetCode-OpenSource%2Frxjs-hooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLeetCode-OpenSource%2Frxjs-hooks/lists"}