rux

Reactive Observable Flux with hot reloading, inspired by redux

Usage no npm install needed!

<script type="module">
  import rux from 'https://cdn.skypack.dev/rux';
</script>

README

rux

Reactive Observable Flux with hot reloading, inspired by redux.

Rux is the bridge connects your React app and your observables together. Rux recognizes your observable implementation by using adapters and comes with built-in support for some popular implementations.

Supported observable implementations:

Planned supports:

Honestly, Rux is not actually Flux, but it's more like redux

Meanwhile, Redux has brought us a step forward in the world of Flux. While Redux still uses some terminologies from Flux, it doesn't look like the Flux we had been used to.

So, What is the biggest step forward that Redux made in Flux?

Stateless stores (soon will be reducers) and pure functions

And that's essentially Rux. However, we love observables, we want to define our app in observables. That's what Rux was made for.

Example (Rx)

Stateless store (reducer)

import Rx from 'rx';

export default function update(initialState = 0, actionObservables) {
  const {
    increment,
    decrement,
    incrementIfOdd
  } = actionObservables;

  const modificationObservable = Rx.Observable.merge(
    increment.map(() => counter => counter + 1),
    decrement.map(() => counter => counter - 1),
    incrementIfOdd.map(
      () => counter => counter % 2 === 0 ? counter : counter + 1
    )
  );

  return modificationObservable
    .startWith(initialState)
    .scan((counterState, mod) => mod(counterState));
}

View

import React, {Component} from 'react';
import Rx from 'rx';
import createProvider from 'rux/provider/rx';
import update from './counter-update';
const Provider = createProvider(React, Rx);

const actionTypes = [
  'increment',
  'decrement',
  'incrementIfOdd'
];

class CounterApp extends Component {
  render() {
    const {
      increment,
      decrement,
      incrementIfOdd
    } = this.props.channels;
    return <p>
      Clicked: {this.props.counter} times
      {' '}
      <button onClick={increment}>+</button>
      {' '}
      <button onClick={decrement}>-</button>
      {' '}
      <button onClick={incrementIfOdd}>Increment if odd</button>
    </p>;
  }
}

class App extends Component {
  render() {
    return <Provider update={update}
                     actionTypes={actionTypes}>
      {(appState, channels) =>
        <CounterApp counter={appState} channels={channels} />}
    </Provider>;
  }
}

export default App;

Motivation

  • Lean implementation - Rux without the adapters is only about 200 LOC and distributed with 0 sub-dependencies
  • Just like Redux, everything is hot reloadable
  • Bring your own observables, build the app in a truly reactive way
  • Externalize the actions - actions are separated from the state
  • Completely separate our application logic from our view code
  • Encourage functional reactive programming - you don't need a book on functional programming, but you do need to know how to use observables

Technically speaking, RxJS, Bacon.js and many other event stream libraries are not exactly FRP. However, every event stream libraries features functional paradigm in their own nature, and FP is always a great tool.

FAQ

My observable implementation is not supported

You can implement the adapter and plug that to the provider. Guideline is WIP. However, you can check the source code of adapter/rx and action/rx for examples.

Inspiration & thanks

  • redux for bringing me ideas
  • Elm for the concept of model/update/view architecture
  • RxJS for the Reactive Programming enlightenment