Package Exports
- observe-component
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (observe-component) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
observe-component
import React from 'react';
import {render} from 'react-dom';
import {observeComponent, fromComponent} from 'observe-component';
const StreamingButton = observeComponent('button', ['onClick']);
function MyButton(props) {
return (<StreamingButton>Hello</StreamingButton>);
}
render(<MyButton />, Document.getElementById('my-app'));
const clickStream =
fromComponent(StreamingButton)
.onValue(() => {
console.log('world!');
});
Installation
npm install --save observe-componentYou will also need to install Kefir and React if they're not already a part of your project:
npm install --save kefir
npm install --save reactAPI
observeComponent(Component, events[])
Returns a higher-order observeComponent with an attached stream of the specified events. Supports all events supported by React's event system.
Example:
const StreamingDiv = observeComponent('div', ['onMouseDown', 'onMouseUp']);fromComponent(observeComponent, [ events[] ])
Returns the stream attached to the StreamableComponent. An optional array of events can be supplied to return a stream only containing those events.
fromComponent streams emit a ComponentEvent object.
Example:
const StreamingDiv = observeComponent('div', ['onMouseDown', 'onMouseUp']);
// will log all 'onMouseDown' and 'onMouseUp' events
fromComponent(StreamingDiv).log()
// will only log 'onMouseUp' events
fromComponent(StreamingDiv, ['onMouseUp']).log();ComponentEvent
The ComponentEvent object contains two properties:
type: a string which identifies the event that has occurred, e.g.: 'onClick', 'onScroll'event: the React librarySyntheticEvent(see: Event System)
But why?
Because Functional Reactive Programming is pretty cool, and so is React. However, React's API is not very FRP-friendly; the necessity to wire up events by hand using buses (or subjects, in RxJS-speak) easily leads us to the Bus of Doom, and in general is finnicky and boilerplate-y to connect an observer to React.
There are also plenty of libraries for connecting streams to React, but very few (none that I've found) that transition React events to streams, enabling a fully functional reactive architecture.
Dependencies
At the moment, observe-component depends directly on Kefir for reactive streams. There is no reason for this. The library could easibly be ported to RxJS/Bacon.js/Fairmont/whatever. Under the hood, it uses Kefir's pool object (basically an equivalent to RxJS' Subject, or Bacon's Bus) to abstract the events into streams; we really never escape the bus, we just hide it. I'm interested in trying to create a portable version that can work with any reactive programming library.
Examples
Components as stateless functions
import React, {Component} from 'react';
import {render} from 'react-dom';
import {observeComponent, fromComponent} from 'observe-component';
const StreamingInput = observeComponent('input', ['onChange']);
function MyApp(props) {
return (
<div>
<div>Hello {this.props.name}!</div>
<StreamingInput type="text" />
</div>
);
}
const nameStream =
fromComponent(StreamingInput)
/* The streams values contain two properties:
'type': The type of the event that was triggered, e.g. 'onChange'
'event': The React library `SyntheticEvent`
*/
.map(({type, event}) => event.target.value)
.onValue((name) =>
render(<MyApp name={name} />, document.getElementById('my-app'))
);
You can stream any kind of component
...as long as you pass event handlers to the appropriate elements. The library simply passes special handlers to React's event system (on<Event>) to abstract them into streams.
class MyWidget extends React.Component {
render() {
return (
<div>
<button onClick={this.props.onClick}>Click me!</button>
<input onChange={this.props.onChange} defaultValue="Change me!" />
</div>
);
}
}
const StreamingWidget = observeComponent(MyWidget, ['onClick', 'onChange']);
const widgetStream =
fromComponent(StreamingWidget)
.onValue(({type, event}) => {
if (type === 'onClick') {
console.log('clicked');
}
else if (type === 'onChange') {
console.log('changed: '+event.target.value);
}
});However, you are strongly encouraged to create streams out of basic components and merge them, rather than manually pass the event handlers yourself.
Also, if we can get away with it, we'd always like to use stateless functions as components. :)
import {merge} from 'kefir';
// Create streamable button and streamable inputs
const StreamingButton = observeComponent('button', ['onClick']);
const StreamingInput = observeComponent('input', ['onChange']);
// Component is simply a function from props to view
function MyWidget(props) {
return (
<div>
<StreamingButton>Click me!</StreamingButton>
<StreamingInput defaultValue="Change me!" />
</div>
);
}
// We construct our application from the two streams
const widgetStream =
merge([
fromComponent(StreamingButton),
fromComponent(StreamingInput),
])
.onValue(({type, event}) => {
if (type === 'onClick') {
console.log('clicked');
}
else if (type === 'onChange') {
console.log('changed: '+event.target.value);
}
});License
MIT