-
Notifications
You must be signed in to change notification settings - Fork 595
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 #1
Comments
For managing simple animations, setting classes is probably the way to go. Using classnames might prove to be helpful. For managing complex animations people should probably use a finite state machine such as f1. For reaction to DOM nodes being removed (e.g. trigger an event to load / unload data) use MutationObservers (caniuse). See Also |
What about firing an ajax request when a view is shown? Can't just put it in the view logic because then it fires again every time the state changes, and you end up with an endless loop if that ajax changes the state. In react, you'd use EDIT: Another thing that makes this hard is the on state change handler, which calls |
Untested, but I reckon on-load does the job correctly. Given that unless the component has mutated it should run just fine. I'd hook a
What do you mean? There should always be a tree - or is this a conflict between the first call to fetch data and the tree being rendered? If this is a bug I'd definitely want to fix this - could you clarify? Thanks! |
In my case, the action is "fetch a list of tables," which is idempotent. The problem is that once they're fetched, the state changes, so the view re-renders, which triggers the fetch, the state change, the re-render, and an endless loop :P
Interesting -- do you mean something like this? module.exports = (params, state, send) => {
const tree = choo.view`<div>Foo</div>`
onload(tree, () => send('tables:fetch'))
return tree
} If so, I may be missing something, because it doesn't appear that changing views triggers the
I tried to get around the "endless loop" using this approach: module.exports = (params, state, send) => {
if (!state.tables.fetched) send('tables:get')
return choo.view`.....`
} This works fine when you load another view and then navigate to this view. But if this view is the first view rendered, you get an error
|
Interesting, I expected the |
I don't think it's an issue with on-load actually -- I put together a test case to illustrate what I believe to be the issue. on-load isn't triggered when you use Let me know if you still think it's an on-load issue and I'll raise it there. |
dammit - yeah, I see what you mean with your repro. Perhaps const hooks = require('./')
const morphdom = hooks(require('morphdom'))
function button () {
const b = document.createElement('button')
b.onadd = console.log.bind(console, 'button added to page!')
b.onupdate = console.log.bind(console, 'button updated in page!')
b.ondiscard = console.log.bind(console, 'button discarded from page!')
return b
} Ideally I reckon this would be part of |
Maybe I'm forgetting something, but...don't we already know when the view is added to the page because it's being done by the router? app.router((route) => [
route('/', require('./views/connect')),
route('/tables', (params, state, send) => {
// fire "componentDidMount" logic here.
// it shouldn't be re-run if state changes, because route hasn't
return require('./views/tables')(params, state, send)
})
]) It would be nice to do that inside the view somehow, but just pointing out that we kind of already have a hook mechanism. EDIT: On further review, this line suggests that it would be called again on re-render |
Just thought of another use case for lifecycle events: a library like Leaflet requires you to initialize it by passing it a DOM node or ID. (1) not sure how you'd do it with the current way views work (if you initialize before |
Feel like we're also not leveraging intermediate lifecycle hooks (as shown in patrick-steele-idem/morphdom#65 (comment)) @timwis I'd memoize the DOM node, check if state has changed and then only re-render if it hasn't; basically always passing the same node. That should pretty much always work I reckon ✨ |
Update: thanks to #27, this approach is possible to enable something like making an ajax call the first time a route/view is triggered |
Looks like shama/on-load#2 might be our savior |
|
yeah comparing |
Hey @yoshuawuyts hopefully I'm just doing it wrong but I was just trying to implement It seems like the second page doesn't trigger |
@timwis no, you're right - it shouldn't - could be because of https://github.com/shama/on-load/blob/master/index.js#L15 not being cleaned up, thus maintaining the reference to the same DOM node; could you open an issue on |
I also have the Leaflet scenario, will |
Hey @knownasilya yes, it should -- I went to put together a demo but came across an issue that I've just reported ^ if you look at the demo there you can get an idea of how |
Really looking forward to that! |
Update for any subscribers to this issue, shama seems to have figured it out. I've posted on that thread with a couple demos. At this point, then, we're just waiting on:
I assume from there there won't actually need to be any changes to choo (other than documentation). |
Looks like |
I guess that means that... we now have lifecycle hooks haha ✨ - should probably formally bump the dep in |
Woohoo! Dep bump posted at max-mapper/yo-yo#34 Just to be clear to anyone stumbling upon this thread, you should now be able to use the const view = (params, state, send) => {
return choo.view`<div onload=${() => console.log('loaded!')}></div>`
} |
Probably worth mentioning in the documentation that the |
@jondashkyle waiiit, what do you mean? edit: derp, I see what you mean. Yeah reckon that's good to mention haha |
Yikes, really? I think it's supposed to work with children elements :-/ at least that was what @yoshuawuyts had in mind, otherwise we could have just triggered it via the router... You know, this is just a guess, but I think |
I put together a little demo repo as there are a few different states in which it’s breaking. There’s a definite possibility that I’m not using it correctly! Hope it’s helpful in some form: https://github.com/jondashkyle/choo-onload/ Worth noting that I get the same errors when doing something which looks like this: const html = require('choo/html')
const onload = require('on-load')
const view = (state, prev, send) {
const div = html`
<div>
Should trigger an on-load event.
</div>
`
onload(div, (el) => {
console.log('Loaded')
}, (el) => {
console.log('Unloaded')
})
return div
} Haven’t engaged an actively developing open source project like this before, so apologies if this would be better suited in the on-load or bel repositories. Would be down to do a little pull request to try looking into it. Worth repeating I’m mad jazzed w/ choo! |
Uh oh, that's not good. I thought by children you meant like: html`<div><div onload=${}</div></div>` which would've been acceptable I reckon. Hmmm, this def looks like a bug. cc/ @shama any idea what could be up? I'm not super familiar with the |
Nice error report @jondashkyle! I've got a fix for the errors. The child onloads not firing is interesting. Basically morphdom isn't adding a |
Can I assume this is mostly resolved then? Are there still any issues - any way I can help? |
@yoshuawuyts Yep all resolved! Fixed and released as Test in bel specifically for this too: choojs/nanohtml@82d9652 |
Understanding how morphdom works, this does appear to be functioning properly, but it deviates from my expectation. If you give this a try, I would anticipate the console to log const choo = require('choo')
const html = require('choo/html')
const app = choo()
const one = (params, state, send) => {
return html`
<div onload=${() => console.log('One')}>
<h2>one</h2>
<a href="/two">/two</a>
</div>
`
}
const two = (params, state, send) => {
return html`
<div onload=${() => console.log('Two')}>
<h2>two</h2>
<a href="/one">/one</a>
</div>
`
}
app.router((route) => [
route('/', one),
route('/two', two)
])
const tree = app.start()
document.body.appendChild(tree) My naive understanding of what’s happening: When Morphdom handles the mutation between the two views, it appears there hasn't been a change on the root Additionally it’s worth noting that something like the following does work as expected, but you loose the optimizations of DOM morphing as both the root element and all children are replaced: const one = (params, state, send) => {
return html`
<el-one onload=${() => console.log('One')}>
<h2>one</h2>
<a href="/two">/two</a>
</el-one>
`
}
const two = (params, state, send) => {
return html`
<el-two onload=${() => console.log('Two')}>
<h2>two</h2>
<a href="/one">/one</a>
</el-two>
`
} If it sounds like I’m understanding this correctly I’m down to dive in and explore options or alternatives to solving this :) MAJOR ups on the 3.0 release! |
@jondashkyle Run I'd like to solve this problem better though. Currently I don't know a good way to identify an element creator with native elements. So when morphing existing DOM nodes, I rely on the In your example above, it thought the same This isn't full proof as I bet adding multiple |
Also dropping a note here, |
Can I conclude that this is now resolved, and that this issue can be closed? (Bit sick atm, haven't kept up with the whole discussion) |
@yoshuawuyts There are still some edge cases I need to get sorted but if the current behavior is satisfactory for the group, then I think it's safe to close. If there are more bugs, feel free to open on on-load and I'll get them sorted. Thanks @timwis, @knownasilya and @jondashkyle for helping me get this fine tuned! Hope you feel better Yosh! |
Status on this? |
@sotojuan thunks are not great; the reason bugs exist is because any given DOM node can only live in one tree at the same time, so conflicts happen. This is heaps annoying and causes events to be called all over the place. I think the best solution would be to have a way to signal to morphdom that it should halt its diffing algorithm at some point; and then create a way to nest multiple diffing trees. There's IRC logs on discussions about this from a few days ago; here-ish. Hope this makes sense |
Welp, now that patrick-steele-idem/morphdom#85 has been merged it's safe to say The handbook should get a section on element lifecycles, memoization and widgets soonish. cache-element is being built which should allow for a clean API to manage updates and events in the I think all of this concludes this issue: lifecycle events exist and work. All that's needed now is to update the ecosystem, but it's no longer an issue. Thanks all for contributing in this thread 🎉 - we've made it! |
docs: fix module name in example
Every once in a while we need hooks to manage things like animations:
max-mapper/yo-yo#18
The text was updated successfully, but these errors were encountered: