-
Notifications
You must be signed in to change notification settings - Fork 397
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lifecycle hooks #927
Comments
hooks
The current So, in theory, these could be the I think we may still need an init that is pre-render. This would be where One thing to keep in mind is that some of these hooks will run multiple times during an instance lifetime. Observers and handlers don't need to be redone (normally), so it would be good to have a place to put them that didn't get called more than once. Conceptually, it seems
If init runs only once, you'd need a beforeRender to redo anything.
is this the detach and insert of the root fragment?
methods or events?I've been leaning to methods, mostly because I like keeping the onRender: function(){
this.fire('rendered')
} There's something attractive about having them all start with new Ractive as extendAt first I thought there was some esoteric reason for // beforeInit goes here
var ractive = new Ractive({})
// init goes here But it is confusing, and in fact the code would be simpler, if we just "extended" before init. I'm all for it. Basically it makes I still have reservations about calling the ractive methods from the template, but I'll continue that discussion on the other thread we started. mixinsSounds good, basically we process all the arguments as options. ES6 splats FTW! |
Personally it took me a while to fully understand how and when each of these hooks can (and can not) be used but meanwhile I'm using all of them quite heavily and wouldn't be too happy if I'd have to refactor all that code ;). I agree with @martypdx on the subject of methods vs. events. I don't know but adjusting the options of a Ractive instance in an event handler somehow just wouldn't feel quite right. Same goes for setting up the events in an event handler. @Rich-Harris I Didn't quite understand why you wrote this:
I always do it like that: MyRactive = Ractive.extend({
// beforeInit, init, complete
// ...
teardown: function() {
// cleanup stuff...
this._super();
}
}); Anything wrong with that approach? |
Thinking on this some more, one of the issues with using methods for the lifecycle events is that it doesn't work well with mixins. Mixins should work autonomously and they may very well need to use the lifecycle events to attach observers, etc. var C = Ractive.extend(mixin1, mixin2, { template: 'xyz' }) I don't think we want to have mixin1's Having easy, declarative events would be ideal for mixins. Which brings me back to the {
on: {
handler1: function () {...}
}
} Assuming we figure out how to distinguish event handlers vs lifecycle events (maybe we have two keywords like {
on: {
init: 'setup',
foo: function(){...}
},
setup: function(){...}
} In this case, all |
fyi - Here's how some other libraries are handling: React uses a specific mixing property .Mixins have a specific lifecycle API largely around mount, unmount, and update. The are specified as a dedicated property on the init options: React.createClass({
mixins: [SetIntervalMixin], // Array of mixins
... Ember uses a dedicated method to create mixins, then they are passed as first param of extend: App.Editable = Ember.Mixin.create({
edit: function() {
console.log('starting to edit');
this.set('isEditing', true);
},
isEditing: false
});
// Mix mixins into classes by passing them as the first arguments to
// .extend.
App.CommentView = Ember.View.extend(App.Editable, {
template: Ember.Handlebars.compile('{{#if view.isEditing}}...{{else}}...{{/if}}')
}); Angular doesn't offer anything specific, extend just copies keys and you can specify multiple sources angular.extend(dst, src1, src2); |
The documentation at http://docs.ractivejs.org/latest/components still references init(), beforeInit(), although the code appears to have been updated. |
Going to close this as most of it has been implemented. Mixins were the one straggler item, let's raise new issues if we still want to add something. (or reopen this issue) |
At present, lifecycle hooks are a bit of a mess - partly for historical reasons, partly a design failure (mea culpa). This is what you have to work with:
To do any cleanup on teardown, you have to do something like this:
If you're not creating a component with
Ractive.extend()
, there's just thecomplete()
method - nobeforeInit()
orinit()
. Harmless, perhaps (you can do thebeforeInit()
work beforenew Ractive()
, and theinit()
work afterwards), but this has proven to be a source of confusion.The problem? Firstly, these hooks are badly named.
init()
andcomplete()
, in particular, offer little clue as to what they're for. Secondly, it's possible to use Ractive without targeting a DOM node - maybe you're using it on the server, maybe you have a viewless instance that you're using for managing state within your app, maybe the instance is responsible for attaching itself to a DOM node that it doesn't yet know about. In any of these cases you would probably expect theinit()
method to be called, but it isn't. It's more like anonRender()
method (it's actually called from within therender()
method).And the
beforeInit()
method is pretty much redundant now that we have option functions for dynamically defining templates and partials and so on.Fixing this isn't hard from an implementation standpoint - it's just a design question that we need to try and get right. It's also an opportunity to consider a couple of related issues.
The hooks themselves
There's a bunch of lifecycle events we need to consider:
new Ractive(...)
)el
option and then callractive.render(el)
, since that returns an equivalentPromise
)(If I've missed any, please point them out!)
Should each of these hooks be a method? Or should some, or all (except the instantiation hook) be events that you subscribe to? A method allows things like
this._super()
, which is potentially useful, and it means one less level of indentation:It also means we have fewer 'system' event names which can't be used for DOM proxy events (e.g.
on-click='change'
is banned, because there's a systemchange
event).On the flip side, events are a simple, well-understood mechanism, and they allow you (and other consumers of a component, not just the component itself) to attach multiple handlers. (In any case, throwing out things like the
teardown
event would break existing code, so they'd have to be deprecated gradually if at all.)If we went with methods (or a hybrid approach), what should they be called? Is
onInit()
,onRender()
and so on the right convention to use?Defining methods at the point of
new Ractive()
It would eliminate the confusion about
beforeInit()
working withRactive.extend()
but notnew Ractive()
if properties of theoptions
object were simply added to the instance - so if you specify anonInit()
method, it gets called:This dovetails nicely with the discussion happening around allowing proxy event directives to call methods directly - we could do things like this without having to faff around creating components:
Mixins
The other thing that would potentially fall out of this is mixin support. If a mixin is defined as a set of methods that are called during the appropriate lifecycle events, you can start adding reusable chunks of functionality that are completely orthogonal to the implementation details of a particular component. I'll be honest, I don't really know what the applications of this could be (logging? persistence?) but it seems like an idea with potential. There's some discussion of it on this issue.
So, that was another long-winded stream of consciousness. Congrats if you made it this far. Would welcome all feedback! Thanks.
The text was updated successfully, but these errors were encountered: