React-Rx: Hooks and Utilities for RxJS Integration
React-Rx is a library designed to seamlessly integrate RxJS Observables into React applications, providing both React Hooks and higher-order component utilities. It excels at handling Observables that emit values synchronously without incurring an immediate re-render on mount, optimizing performance. The library, currently at stable version 4.2.2, maintains a consistent release cadence with frequent updates for bug fixes and dependency upgrades, often related to React Compiler advancements. It offers full TypeScript support and differentiates itself by providing two distinct yet powerful patterns: a set of `useObservable` hooks for functional components and a `reactiveComponent` utility for a more declarative, component-based approach. Users are encouraged to choose one style rather than mixing them within the same component, as they represent different programming paradigms. It requires `rxjs` version 7 or higher and `react` version 18.3 or 19.0.0-0, ensuring compatibility with modern React ecosystems.
Common errors
-
Error: Can't resolve 'rxjs' in 'your-project/node_modules/react-rx'
cause The `rxjs` peer dependency is missing or an incompatible version is installed.fixInstall `rxjs` explicitly: `npm install rxjs@^7` or `yarn add rxjs@^7`. -
TypeError: (0, _react_rx.useObservable) is not a function
cause This usually indicates a CommonJS (`require`) attempt to load an ES Module (`react-rx`) or a bundler misconfiguration in an older project.fixVerify that your project and bundler (e.g., Webpack, Rollup) are configured to handle ES Modules (`import` statements). Ensure your `package.json` has `"type": "module"` if it's an ESM project, and use `import { useObservable } from 'react-rx';`. -
React Hook 'useObservable' cannot be called inside a class component. React Hooks can only be called from a function component or custom Hook.
cause `useObservable` is a React Hook and adheres to React's Rules of Hooks.fixConvert your component to a functional component or refactor the logic to be used within a custom hook, or consider using `reactiveComponent` for class-like reactive patterns if a class component is strictly required.
Warnings
- breaking Older versions of `react-rx` (prior to v3) might have supported CommonJS (`require()`) imports. Since v3, the library is primarily designed for ES Modules, requiring `import` statements. Attempting to `require()` this package in a pure ESM context or misconfigured bundler can lead to runtime errors.
- gotcha Mixing `useObservable` hooks and `reactiveComponent` within the same React component is not recommended due to their fundamentally different programming styles and underlying implementations. Choose one paradigm for consistency and clarity.
- gotcha Incorrect peer dependency versions for `react` or `rxjs` can lead to runtime issues or unexpected behavior. `react-rx` relies heavily on specific versions of these libraries.
- gotcha When `useObservable` is provided with an `initialValue` and the observable emits synchronously at subscription time, the synchronously emitted value will be used, ignoring the `initialValue`. This can be unexpected if not aware of RxJS's synchronous emission capabilities.
Install
-
npm install react-rx -
yarn add react-rx -
pnpm add react-rx
Imports
- useObservable
const useObservable = require('react-rx').useObservable;import { useObservable } from 'react-rx'; - useObservableCallback
import useObservableCallback from 'react-rx/useObservableCallback';
import { useObservableCallback } from 'react-rx'; - reactiveComponent
import reactiveComponent from 'react-rx/reactiveComponent';
import { reactiveComponent } from 'react-rx'; - Observable
import { Observable } from 'react-rx';import { Observable } from 'rxjs';
Quickstart
import React, { useMemo } from 'react';
import { useObservable } from 'react-rx';
import { interval, map, take } from 'rxjs';
interface CounterProps {
startAt: number;
intervalMs: number;
}
function ObservableCounter({ startAt, intervalMs }: CounterProps) {
const counterObservable = useMemo(
() => interval(intervalMs).pipe(
map(i => startAt + i),
take(10) // Stop after 10 emissions to prevent infinite updates
),
[startAt, intervalMs]
);
const count = useObservable(counterObservable, startAt); // Initial value
return (
<div>
<h2>Observable Counter</h2>
<p>Current count: {count}</p>
<p>Will count from {startAt} every {intervalMs}ms for 10 times.</p>
</div>
);
}
export default function App() {
return (
<div>
<h1>React-Rx Demonstration</h1>
<ObservableCounter startAt={0} intervalMs={1000} />
<ObservableCounter startAt={100} intervalMs={500} />
</div>
);
}