Making React reactive
Strongly inspired by cycle.js and Flux-type architectures. Encourages single direction dataflows within react components.
Components should not access global state. Instead, state is injected into components using Rx Observables and subscribers. A component can be seen as a dataflow component with inputs and outputs:
Component inputs are Observables
Component outputs are Subscribers
Both inputs and outputs are injected into components. Outside the components, the output of the component can be connected to the input. This creates a unidirectional cycle: Output of the component emits events to the injected subscriber, to which the inputs listens to, which updates the component. A component can be visualized like this:
Using Rx, these entities can be modelled as follows:
Subscriber
is aRx.Subject
Input
does a scan onSubscriber
to process events and update the state.Output
is the onNext method ofSubscriber
.
Or in code:
let $subscriber = new BehaviourSubject(initialState);
let input$ = $subscriber.scan((prevState, event) => {
return calculateNewState(prevState, event);
});
let component = <Component input$={input$} $subscriber={$subscriber.asSubscriber()}/>;
A convention that helps me remembering what is what is to annotate an Observable with a $
behind it (in the sense that it 'produces') and annotate Subscribers with a $
in front (in the sense that it 'consumes').
Do note that you cannot actually pass $subscriber.onNext
! React modifies the this object to the component if you pass it a function. This can be worked around by calling $subscriber.asSubscriber()
.
The nice thing about this that all inputs and outputs are explicit. You can test indivual components easily by attaching mock observables and subscribers. This structure makes composition really easy. All you need to do is wire the correct inputs and outputs together. This is illustrated in the Todo example:
npm install rx-react-components
npm install -g gulp
git clone git@github.com:besuikerd/react-rx-components.git
cd react-rx-components
npm install
gulp dev
Starts up a webpack dev server on port 3000. Examples can be accessed at http://localhost:3000/webpack-dev-server/examples
RxComponent(cls, [propName], [valueName])
RxComponent
is a higher order class that decorates a react component that accepts a Rx observable as an input.
The decorated component expects a prop called state$
that contains the observable that defines the state of the component.
The current state of the observable can be accessed through the component state: this.state
. If the observable emits objects, their keys will be bound in this.state
if the observable emits other types, these will by default be bound to this.state.value
Optionally a property name that is used to bind the observable to can be passed as a second argument to the RxComponent function.
Furthermore a second optional value name can be given to which the component will bind a value to whenever you have an Observable that emits single items instead of objects.
import RxComponent from 'react-rx-components';
import React from 'react';
import ReactDOM from 'react-dom';
import Rx from 'rx';
let obs$ = Rx.Observable.interval(1000)
.startWith(0)
.map((x => ({value: x})));
let Component = RxComponent(class Component extends React.Component{
render(){
return <div>{this.state.value}</div>
}
});
ReactDOM.render(<Component state$={obs$}/>, document.getElementById('content'));
let Component = RxComponent(class Component extends React.Component{
...
}, 'otherPropName');
let jsx = <Component otherPropName={obs$}>
More examples can be found in examples