Skip to content

Instantly share code, notes, and snippets.

@brandonbloom
Last active August 29, 2015 14:04
Show Gist options
  • Save brandonbloom/56be8fcd4989f7396a1a to your computer and use it in GitHub Desktop.
Save brandonbloom/56be8fcd4989f7396a1a to your computer and use it in GitHub Desktop.
/** @jsx React.DOM */
var Bubbler = React.createClass({
render: function() {
return <input type="button" onClick={this.props.onFoo} value="Click me"/>;
}
});
var Handler = React.createClass({
onFoo: function() {
alert("handled!");
},
render: function() {
return <Bubbler onFoo={this.onFoo}/>;
}
});
React.renderComponent(<Handler/>, document.body);
/** @jsx React.DOM */
var Bubbler = React.createClass({
render: function() {
return <input type="button" onClick={this.props.onFoo} value="Click me"/>;
}
});
var Handler = React.createClass({
effects: {
foo: function() {
alert("handled!");
}
},
render: function() {
return <Bubbler onFoo={this.effects.foo}/>;
}
});
React.renderComponent(<Handler/>, document.body);
/** @jsx React.DOM */
var Bubbler = React.createClass({
render: function() {
return <input type="button" onClick={this.props.onFoo} value="Click me"/>;
}
});
var Handler = React.createClass({
handlers: function() {
return [
[this.props.eff, function() {
alert("handled!");
}]
];
},
render: function() {
return this.props.children;
}
});
var Wrapper = React.createClass({
effects: {
foo: newEffect()
},
render: function() {
return <Handler eff={this.effects.foo}><Bubbler onFoo={this.effects.foo}/></Handler>
}
});
React.renderComponent(<Wrapper/>, document.body);
@DavidBruant
Copy link

Context

Sharing some context about my initial tweet. I'll answer some points about the gist afterwards.

I'll probably say things that seem obvious in this section, but that'll explain my thought process.

Events are an interesting concept as they allow external parts of a program to independently listen to what's happening to a given object whenever they want (and stop listening to the object events, whenever they want to as well).

"independently" is important, because it helps decoupling. Different components don't need to coordinate which reduces opportunity for them to interfere with one another. (Note that the DOM event model is terrible, because listeners can interfere with one another via event.stop(Immediate)Propagation. Anyway...). This is akin to different objects having a reference to a common object can independently call its methods.

"whenever they want" is important too. There is no reason to be forced to listen all the time from the beginning of a component life, nor no reason to listen until the end. For instance, everyone should remove their DOMContentLoaded listener since this event will only ever happen once during the entire document lifetime. Any other call to this listener would be a mistake (but very few people do).
In an application, it also happened to me recently to reimplement some sort of range picker. I would add an event listener to mousemove only after a mousedown on the element and remove the listener on mouseup.
Both of these examples are fairly impossible to implement in React as it is. The only way is to pass a callback unconditionally and make that function a noop based on some condition. I'd rather stop listening when relevant.

React lacks events in the sense that only one listener can be set to a component (via this.props. Why aren't props explicitely passed as an argument to render by the way? Anyway, question for another time) and only at component creation time. Several objects which want to listen to a particular event can, but they have to coordinate with the parent which will need to expose a way to add/remove event listeners, take care of all that internally so the callback passed to the child component does call the right set of callbacks. Not impossible, but burdensome.

On the proposal

I'm not sure the proposal solves the problem of multiple independant listeners, nor the "I want to listen whenever I want" constraint.

I understand the motivation behind how React is currently modeled.
However, I wonder if these motivation make completely impossible to have a (non-bubbling) event system with independent listeners that can be added and removed at any time.

@n1k0
Copy link

n1k0 commented Jul 20, 2014

I would add an event listener to mousemove only after a mousedown on the element and remove the listener on mouseup

Sounds complicated… Is this for performance reason? Anyway you're right, there's no obvious way to achieve this using event listeners passed as attributes in React. I'm just challenging the idea of this need being mainstream enough for the team to work on this… Honestly, I don't have a clue.

@DavidBruant
Copy link

I ended up implementing the range thing I was talking about https://davidbruant.github.io/inline-range-demo/ Click on the number and drag left and right to increase/decrease the value.
It can be implemented in React, but in a way that is more elegant in my opinion, because of the lack of a proper event system.

@n1k0
Copy link

n1k0 commented Jul 20, 2014

http://jsbin.com/vapay/4/edit

It's really matter of calling the handleMouseMove method and testing for this.state.dragging; it's probably less performent, but not that much I think (though I'm not sure). This could probably be microbenchmarked, oh well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment