lilactown / observe-component Goto Github PK
View Code? Open in Web Editor NEWA library for accessing React component events as reactive observables
License: MIT License
A library for accessing React component events as reactive observables
License: MIT License
observe-component
currently does not support listening to component lifecycle events.
It might be more useful to use { type, value }
instead of { type, event }
. Some events send back a ReactEvent
while others send raw values or other object types (e.g. React Native's TextInput onChangeText
responds with a string value of the input).
This would be a breaking change, though, so would require a major version increment.
Right now, creating the ObservableComponent
and obtaining the observable attached to the component is done in two steps:
// create observable component
const MyComponent = observeComponent('div')('onClick');
// obtain the observable attached to it
const myObs$ = fromComponent(MyComponent);
observeComponent
could be changed to instead return both the component we are observing, as well as the observable attached:
const { component, observable } = observeComponent('div')('onClick');
This would remove the need for fromComponent
, however it would require users to use object destructuring and renaming:
const {
component: MyComponent,
observable: myObs$,
} = observeComponent('div')('onClick');
const {
component: OtherComponent,
observable: otherObs$
} = observeComponent('input')('onChange');
It could also be done by returning an array instead of an object to avoid the verbosity of renaming the object properties:
const [MyComponent, myObs$] = observeComponent('div')('onClick');
However, this would be less ergonomic for people who may be utilizing this library without es2015+.
fromComponent
function, which is slightly redundant and potentially confusing to new users.Rx.Observable.from()
, Kefir.fromCallback/fromEvents/fromPoll
, etc...)Currently, observeComponent(SomeComponent)
prevents accessing of any static methods on SomeComponent
Have we considered using named exports for specific methods so that we don't immediately require the entire RxJS bundle on behalf of the user (of this library)?
It would mean replacing this:
var Rx = require("rxjs/Rx");
...
subjectFactory: function () { return new Rx.Subject(); },
With this:
var Subject = require("rxjs/Subject").Subject;
require('rxjs/add/operator/filter');
...
subjectFactory: function () { return new Subject(); },
The benefit is that we are no longer pulling in the entire RxJS library for this library to work. But the downside is that the user needs to manually add in their own methods (because the observable passed to them will be "barebones").
For example, the observable the user gets back from fromComponent()
will not have methods like .share()
or .debounceTime()
. They would need to add it themselves explicitly like this:
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/debounceTime';
One option to get around this is to simply provide both options to the user. Maybe something like this:
// Imports the whole RxJS bundle
import { observeComponent, fromComponent } from 'observe-component/rxjs/full';
// Imports only the necessary objects and methods
import { observeComponent, fromComponent } from 'observe-component/rxjs';
The library might get bigger, but this gives the developer much more control if he wants to keep the bundle size small for production
Currently, using observe-component
, one cannot pass UI state through the stream without manually adding handlers into the view code. E.g.:
const ClickableLi = observeComponent('onClick')(({ name }) => <li>{ name }</li>);
function View() {
return (
<ul>
{['John', 'Will', 'Marie'].map((name) => <ClickableLi name={name} />)}
</ul>
)
}
fromComponent(ClickableLi)
.subscribe((event) => {
// there is no way to get the UI state (which name was clicked) other than
// accessing the DOM directly using `event.value.target`
});
Currently, the only way to get the desired behavior is by manually calling the event handlers passed to the observed component with the desired data:
const ClickableLi = observeComponent('onClick')(({ name, onClick }) =>
<li onClick={onClick(name)}>{ name }</li>
);
// ...
fromComponent(ClickableLi)
.subscribe((event) => {
console.log(event.value); // "John"
});
This is very undesirable, as the whole point of the library is to remove the need to deal with event handling in our view code!
A possible change in the API could provide seamless propagation of UI state (props) through the ComponentEvent
.
The simplest solution would be to propagate props through an additional field in ComponentEvent:
class ComponentEvent {
type: string,
value: React.SyntheticEvent,
props: any,
};
Although this begs the question why value
is named as such, since the point of changing it previously was to reflect the fact that one may want to pass other things besides the synthetic event back...
class ComponentEvent {
type: string,
event: React.SyntheticEvent,
props: any,
};
In theory, this should allow one to capture any necessary UI state through the props of the component being acted on.
I had a little bit of trouble getting started in the beginning. I didn't realize that fromComponent
and observeComponent
were both needed. In any case, I've created the following example for other people who are also using React and RxJS v5 with this library.
@Lokeh feel free to add this example to your docs, and thanks for the library! Please let me know if there can be any improvements to the following code.
import React from 'react';
import { observeComponent, fromComponent } from 'observe-component/rxjs';
// Create the component with the listeners we want
const TextArea = observeComponent('onInput')('textarea');
export default class MyComponent extends React.Component {
constructor() {
super();
this.state = { typing: false };
}
componentDidMount() {
// Create stream for when user is typing
this.inputEvents$ = fromComponent(TextArea, 'onInput').share();
// Create stream for when user has been idle for 1 second
this.idle$ = this.inputEvents$.debounceTime(1000).share();
// Subscribe to invoke the streams and handle the events
this.inputEvents$.subscribe(() => this.setState({ typing: true }));
this.idle$.subscribe(() => this.setState({ typing: false }));
}
componentWillUnmount() {
this.inputEvents$.unsubscribe();
this.idle$.unsubscribe();
}
render() {
return (
<div>
<TextArea cols="30" rows="10" />
<div>{this.state.typing ? 'User is typing...' : 'User is idle'}</div>
</div>
);
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.