How the Redux Observable Method Supports Reactive Library Integration

Redux’s observable method implements the TC39 Observable proposal, exposing a minimal subscribe interface that allows reactive libraries like RxJS to treat the store as a stream of state changes.

The observable method in the reduxjs/redux repository provides a standards-compliant bridge between Redux’s imperative store and reactive programming paradigms. By implementing the TC39 Observable proposal, this method enables seamless integration with libraries like RxJS, most.js, and zen-observable without requiring additional adapters.

What Is the Redux Observable Method?

The observable method is a minimal Observable implementation attached to every Redux store. It follows the TC39 Observable proposal specification, providing a subscribe method that emits the current state immediately upon subscription and then forwards all future state changes.

Unlike full-featured reactive libraries, Redux’s implementation is intentionally lightweight. It resides in src/createStore.ts and provides only the essential Observable contract: a subscribe method and a self-referencing [Symbol.observable]() method.

How the Observable Method Works Under the Hood

The observable() Factory in src/createStore.ts

The implementation lives inside the createStore function in src/createStore.ts. The observable() function constructs and returns the Observable object:

// src/createStore.ts
function observable() {
  const outerSubscribe = subscribe
  return {
    subscribe(observer: unknown) {
      if (typeof observer !== 'object' || observer === null) {
        throw new TypeError('Expected the observer to be an object.')
      }

      function observeState() {
        const observerAsObserver = observer as Observer<S>
        if (observerAsObserver.next) {
          observerAsObserver.next(getState())
        }
      }

      observeState()
      const unsubscribe = outerSubscribe(observeState)
      return { unsubscribe }
    },

    [$$observable]() {
      return this
    }
  }
}

The function captures the store’s native subscribe function as outerSubscribe, allowing the Observable to tap into Redux’s existing subscription mechanism.

Subscription Protocol and Immediate Emission

The observable method follows a specific emission pattern:

  1. Immediate state emission – Upon subscription, observeState() is called immediately, pushing the current store state to the observer via observer.next(getState()).
  2. Future update forwarding – The outerSubscribe(observeState) call registers the observer to receive all subsequent state changes through Redux’s native subscription system.
  3. Unsubscription – The returned object contains an unsubscribe method that delegates to the store’s unsubscribe function.

This behavior ensures that reactive libraries receive the current state as a "cold" emission followed by "hot" updates, matching the expected Observable contract.

Symbol.observable Interoperability

The observable method implements the required [Symbol.observable]() method (aliased as $$observable in the source):

[$$observable]() {
  return this
}

This self-referencing method satisfies the TC39 specification requirement that observable[Symbol.observable]() must return the same observable instance.

The symbol itself is resolved in src/utils/symbol-observable.ts, which provides a fallback to the string '@@observable' when the native Symbol.observable is unavailable:

// src/utils/symbol-observable.ts
export default (() =>
  (typeof Symbol === 'function' && Symbol.observable) || '@@observable')()

TypeScript Types and Store Integration

The type definitions in src/types/store.ts formally declare the Observable contract:

// src/types/store.ts
export type Observable<T> = {
  subscribe: (observer: Observer<T>) => { unsubscribe: Unsubscribe }
  [Symbol.observable](): Observable<T>
}

The store object itself is constructed with the observable method attached to the $$observable symbol:

// src/createStore.ts
const store = {
  dispatch,
  subscribe,
  getState,
  replaceReducer,
  [$$observable]: observable
} as Store & Ext

This allows reactive libraries to access the observable via store[Symbol.observable]() or simply store[Symbol.observable] depending on their consumption pattern.

Integrating Redux with RxJS and Other Reactive Libraries

Converting the Store to an RxJS Observable

Because Redux implements the Observable spec, RxJS can treat the store as a first-class Observable source using the from creation operator:

import { from } from 'rxjs'
import { filter, map } from 'rxjs/operators'
import { createStore } from 'redux'

const store = createStore(counterReducer)

// RxJS recognizes store[Symbol.observable]()
const state$ = from(store)

// Apply RxJS operators
state$
  .pipe(
    filter(state => state.count > 0),
    map(state => state.count)
  )
  .subscribe(count => console.log('Positive count:', count))

The from operator automatically detects the Symbol.observable property and uses the subscribe method to bridge Redux state changes into the RxJS stream.

Using most.js and Other TC39-Compliant Libraries

Other reactive libraries that support the TC39 proposal can consume the Redux store directly:

import most from 'most'
import { createStore } from 'redux'

const store = createStore(reducer)

// most.js checks for Symbol.observable
const stateStream = most.from(store)

stateStream.observe(state => {
  console.log('State update via most.js:', state)
})

The same pattern works with zen-observable and other spec-compliant implementations, demonstrating the universal interoperability provided by the observable method.

Summary

  • TC39 Compliance: The observable method implements the TC39 Observable proposal, providing a subscribe method and [Symbol.observable]() self-reference.
  • Location: The implementation resides in src/createStore.ts, with type definitions in src/types/store.ts and symbol polyfill in src/utils/symbol-observable.ts.
  • Emission Pattern: The observable immediately emits the current state upon subscription, then forwards all future state changes through Redux's native subscription system.
  • Interop: Reactive libraries like RxJS, most.js, and zen-observable can consume the store directly using from(store) or equivalent operators that detect Symbol.observable.

Frequently Asked Questions

What is the TC39 Observable proposal?

The TC39 Observable proposal is a standardization effort to add native Observable support to JavaScript. It defines a minimal interface where an object must provide a subscribe method that accepts an observer (with next, error, and complete methods) and return a subscription object with an unsubscribe method. The proposal also requires a [Symbol.observable]() method that returns the observable itself, enabling recursive unwrapping by reactive libraries.

How does Redux's observable method differ from RxJS?

Redux's observable method is a minimal implementation that only supports the core TC39 contract, whereas RxJS provides a full-featured reactive programming library with hundreds of operators and advanced scheduling capabilities. The Redux implementation specifically emits the current state immediately upon subscription and then forwards future state changes, but it does not include operators like map, filter, or debounce. RxJS can wrap Redux's minimal observable using from(store) to add these capabilities.

Can I use the observable method without installing RxJS?

Yes, the observable method works independently of RxJS or any other reactive library. You can access it directly via store[Symbol.observable]() and subscribe using the native subscribe method with a plain observer object:

const observable = store[Symbol.observable]()
const subscription = observable.subscribe({
  next: (state) => console.log(state)
})

This approach requires no external dependencies beyond Redux itself, though you must implement your own stream processing logic since the native observable provides only basic subscription capabilities.

Why does the observable emit the current state immediately?

The immediate emission of the current state upon subscription follows the "cold then hot" pattern common in reactive programming. When a subscriber connects to the Redux store, it receives the current state as an initial value (the "cold" emission), ensuring the observer has immediate access to the data without waiting for the next dispatch. Subsequent emissions occur whenever the state changes (the "hot" updates), forwarded through Redux's native subscription system. This behavior ensures consistency with the TC39 proposal and matches the expectations of reactive libraries that assume observables emit initial values upon subscription.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →