Introducing Redux-Fusion: An alternative approach to React-Redux’s connect Method for RxJS Observable State & Components via Recompose

D. Benjamin Ipsen
6 min readApr 1, 2017

--

TL;DR Redux-Fusion is a higher order component that exposes store.dispatch and combines Redux state as an observable with observables produced by UI events on a wrapped component and then returns combined observable values with event handlers to the wrapped component as props.

Did that hurt your brain a bit? Great! Read on friend.

In my last bit, I wrote about two of the more important things our team learned on our journey down the Redux path to enlightenment. One of those two was thinking of actions as streams or more commonly “effects”.

I wrote a simple example using redux-observable and since then I’ve been digging deeper into Observables themselves. I won’t say much about reactive programming in this post other than this: It’s awesome. I’m still a beginner but it’s easy to get hooked once the basics sink in.

With redux-observable as my starting point, I wondered what other aspects of a react-redux application could benefit from observables. In that research I came across some Rx based projects that could replace Redux entirely. I was intrigued by a generalized yet valid point: The basic premise of Redux can be expressed with RxJS in one line:

actions$.scan(reduce).subscribe(view);

This lead me down an interesting path searching for a better “Beyond Redux” paradigm altogether. I even went so far as to scratch the surface of Cycle.js in addition to writing things with other full replacements.

Still, I kept coming back to how awesome Redux is and it’s very hard to overlook its well deserved market share.

The high point in my quest for observable bliss came when I stumbled upon Recompose’s Observable utilities. I got pretty excited about mapPropsStream and createEventHandler which make it super easy to convert UI events to observable streams.

I set out to do two things using new tools and plain old Redux:

  1. Subscribe to component UI events as observable streams and dispatch.
  2. Stream Redux state as an observable to a component as props.

As stated above, Recompose gets us one step closer to our first goal. Here is a slightly modified example of mapPropsStream from the Recompose docs:

const enhance = mapPropsStream(props$ => {
const { handler, stream: handler$ } = createEventHandler()
const timeElapsed$ = Observable
.interval(2000)
.merge(handler$.map(() => 'clicked!'))
.startWith('two seconds...')
return props$.combineLatest(timeElapsed$, (props, timeElapsed) => ({
...props,
handler,
timeElapsed
}))
})
const Timer = enhance(({ handler, timeElapsed }) =>
<div>
Time elapsed: {timeElapsed}
<button onClick={handler}>Click me</button>
</div>
)
// Time elapsed: 'two seconds...', 0, 1, 'clicked!', 2 ...

As you can see enhance() is now a higher order component that will pass the observable values we composed as props to the wrapped component.

As for our second task, Redux supports Observable.from(createStore())out of the box so that’s nice and easy as well.

Let’s slightly refactor the above example to look a bit more like your everyday react-redux container:

const mapStreamToProps$ = (props$) => {
const { handler, stream: handler$ } = createEventHandler()
const timeElapsed$ = Observable
.interval(2000)
.merge(handler$.map(() => 'clicked!'))
.startWith('two seconds...')
return props$.combineLatest(timeElapsed$, (props, timeElapsed) => ({
...props,
handler,
timeElapsed
}))
}
const View = ({ handler, timeElapsed }) => (
<div>
Time elapsed: {timeElapsed}
<button onClick={handler}>Click me</button>
</div>
)
const Timer = mapPropsStream(mapStreamToProps$)(View)
// ^ looks like connect!

Now, we need to make the Redux state stream and dispatch method available in our mapStreamToProps$ function so that we can send it as props and allow our UI event streams to call dispatch.

I first tired to import the store directly and compose like this:

import store$ from '../redux/createStore'const $mapStreamToProps => store$ => props$ => { ... }
...
const Timer = mapPropsStream(mapStreamToProps$(store))(View)

This works, but it’s not what we want. Redux discourages use as a singleton for a few reasons which I won’t get into, but they ain’t wrong.
Don’t do it.

So, what’s the RightWay™? In any existing Redux app we have the store in context all the time. (Duh! That’s how connect works). So what’s a clean way to extract that and use it? Another higher order component you say? Damn straight skippy!

Redux-fusion was born:

export default propStream$ => (StreamedComponent) => {
class ComponentFromStream extends Component {
render() {
const { store } = this.context;
return createElement(
mapPropsStream(
propStream$(Observable.from(store), store.dispatch)
)(StreamedComponent)
)
}
}
ComponentFromStream.contextTypes = {
store: PropTypes.object
}
return ComponentFromStream
}

That’s the whole thing! (with some error reporting removed) This simple HOC allows us to wrap the props$ stream function with another function that receives state$ stream via Observable.from(store)and dispatch from the context. Here’s how it looks in action:

import React from 'react'
import { createEventHandler } from 'recompose'
import fuse from 'redux-fusion'
import { someReduxAction } from '../redux/actions'

const hello$ = (state$, dispatch) => (props$) => {
const {
handler: handleClick,
stream: click$
} = createEventHandler()

click$
.debounceTime(300)
.subscribe(() => dispatch(someReduxAction()))

const hello$ = state$
.pluck('hello')
.map(val => `Hello ${val}`)

return props$.combineLatest(hello$, (props, hello) => ({
hello,
handleClick
}))
}

const Hello = ({ handleClick, hello }) =>
(
<div>
<h3>{hello}</h3>
<button onClick={handleClick}>Click Me</button>
</div>
)

const HelloWorld = fuse(hello$)(Hello)

See how we debouncedTime()on the click then sent out an action via dispatch? This takes the place of mapDispatchToProps but with the power of reactive programming. Notice .pluck('hello')? This takes the place of mapStateToProps but also wields the power of Rx.

If you don’t have any exposure to RxJS that may not look super friendly but my hope is that those who are familiar with React, Redux and RxJS and can see the radness.

How is this different than redux-observable?

Redux observable is a middleware library that creates a stream of redux actions e.g. {type: A, payload: b }(FSAs) whereas redux-fusion is a combination of observable UI events e.g. onChange, onClick, onBlur etc. and the Redux state itself as an observable (state$).

Redux-fusion should be thought of as a “RxJS powered replacement for react-redux connect” rather than “one of ‘em fancy async middleware libraries” which of course you are encouraged to use along with redux-fusion. You can probably venture a guess at which I prefer ; )

You can use async observables in your “fuser functions”, however dispatching values out of streams (i.e. fetched data) is a bit tricky and is really a job for your actions… or Apollo, or whatever.

If you had some kind of isolated container state you could stream props to it asynchronously with fusion. This steps outside the Redux pattern, but serves as a cool Rx example:

export default state$ => (props$) => {
const {
handler: requestData,
stream: request$
} = createEventHandler()
const hello$ = request$
.switchMap((id) =>
Observable
.fromPromise(global.fetch(`/examples/${id}`)))
.concatMap(res => Observable.fromPromise(res.json()))
.startWith('Loading...')
return props$.combineLatest(hello$, (props, hello) => ({
hello,
requestData
}))
}

Notice how there aren’t any calls to dispatch? Where is the point at which you would you get data from the response then dispatch it while maintaining the observable flow? I’m not super sure either.

What I view as the coolest outcome of this research and ultimately redux-fusion is that you can drop it into any existing app. There is ZERO additional buy-in. No refactoring required! As long as your app root is wrapped with Redux store<Provider> it will JustWork.

That means you can keep your reducers, components, actions, thunks, sagas containers or anything else as-is. With Fusion, you simply get some extra Rx sauce in the ability to flow control your component events and state (and merge those two stream sources!). Hint: Combining Redux state stream and user input streams is super useful in forms and realtime… more on that soon!

What’s next?

There’s still a strong move towards declarative fetching from a GraphQL backend (Relay et al). I have yet to dive into GraphQL and its bindings but from what I have skimmed so far, I feel that a “fuser function” may be a great place to express data shape consumed by the fused component and then stream that data as observables… ?

I also plan to set up a redux-fusion-examples repo with some fuser function patterns which can be used in the wild. I’ll update this article when I have those in place.

Side Note: I’m leaving tomorrow (April 2 2017) for a remote area in Indonesia so if I don’t respond to any comments right away sorry. I will do so when I return!

Thanks for reading, ‘till next time!
🐧

--

--