-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
fix: reset title element to previous value on removal #14116
base: main
Are you sure you want to change the base?
Conversation
commit: |
I wonder what other frameworks do here? However, it's maybe the correct behaviour if another |
|
preview: https://svelte-dev-git-preview-svelte-14116-svelte.vercel.app/ this is an automated message |
const previous = document.title; | ||
document.title = text; | ||
teardown(() => { | ||
document.title = previous; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't been able to break this, but it feels like we should put the setup in an effect so that there's no possibility of another component setting the title to a new value immediately before the teardown nukes it. might just be superstition
const previous = document.title; | |
document.title = text; | |
teardown(() => { | |
document.title = previous; | |
}); | |
const previous = document.title; | |
effect(() => { | |
document.title = text; | |
return () => { | |
document.title = previous; | |
}; | |
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The effect does nothing, since text is a static value; I kept the "add document.title = .. to the shared effect" part within the compiler which is why it's updating still. The assumption is that it's rare that people have a reactive title. But we can move it into here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not about reactivity, it's about preventing this sequence of events from being a possibility:
- component A is removed and component B is created at the same moment
- component B sets
document.title
- component A is removed and reverts
document.title
to whatever it was beforeA
was created
That may already be impossible, hence 'superstition'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In which way does adding a render_effect
help with this? The render effect is called synchronously, and teardown
is a render effect under the hood.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was more of a belt-and-braces thing — see #14116 (comment). but it also means that if/when we introduce forking, we won't set the title until the fork is committed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I was going on about the above suggestion. If it's a render_effect, then that's not right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Surely render effects other than blocks wouldn't run until forks are committed? The whole point is to not update the DOM until then
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe. Too early to say for sure how that will all end up. Let's just keep it as is then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But keeping it as is currently, i.e. using a render_effect
for the thing instead of just a teardown
does nothing, as I've just explained.
Not gonna die on this hill, maybe I don't understand what this proofs against exactly, and using teardown is a microoptimization regardless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, no-one is updating the title at 60fps. given that there's potential upside and no real downside i reckon we can just keep things as they are here — this is ready to merge from my perspective
What we could also do: Last one wins, always, i.e. it doesn't matter whether or not A is first and updates after B comes in, if B is after A then B always takes precedence. In DEV mode we could also warn then "hey there are multiple competing, and the last one which is currently X wins". |
That gets complicated with error boundaries though, no? What if you trigger an update and the title changes, but then an error occurs? Is this warning then useful here? |
that feels like a good reason to put the assignment to |
|
document.title = text; | ||
|
||
return () => { | ||
document.title = previous; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should handle another edge case - thoughts?
document.title = previous; | |
// Preserve later updates to title from other sources | |
if (document.title === text) { | |
document.title = previous; | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which case would this prevent? It actually seems to me like this might make bugs more likely, since if a component had a <title>
and also contained a component that set document.title
in a user effect, it wouldn't revert either when the parent was removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This handles the case where A
sets the title, then B
is mounted and sets the title, then A
updates the title (in other words it contains something like <title>{foo}</title>
), then B
is removed - in that case we wouldn't want the title to be reverted back to what A
had initially, we want to preserve the title.
Setting document.title
in a user effect manually feels a bit like "working against the framework", you should just do that declaratively.
They are both edge cases either way, so I'm ok with not adding this suggestion.
const previous = document.title; | ||
document.title = text; | ||
|
||
return () => { | ||
document.title = previous; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can also do this, would handle the case described in https://github.com/sveltejs/svelte/pull/14116/files#r1876874053
const previous = document.title; | |
document.title = text; | |
return () => { | |
document.title = previous; | |
const previous = document.title; | |
const own = {}; | |
// @ts-expect-error | |
document._last_title_setter = own; | |
document.title = text; | |
return () => { | |
// @ts-expect-error | |
if (document._last_title_setter === own) { | |
document.title = previous; | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at how the code is generated, we should switch it up anyway - right now if your title would contain dynamic content, you would set it two times on an update - once to revert to the previous value, then right away again for the updated value. Wasteful.
Generally speaking, what's supposed to happen in a case like the following?
|
I think the solution to my proposed scenario would be to centralize the "history" of backups. The owner, which will be at the top of the history stack, will pop the next one, and as components unload, they remove the next item in the history which is their contributed value. In the example, as Component B unloads, it takes backupC out because it contains its contribution to the history of titles. |
Assuming my proposal is carried into reality, here's another scenario:
At this point, say all 3 components are loaded and they submit titles reactively during the course of the application. What should happen here? Possibilities:
If the latter, then my proposal needs ammending: Now the component needs to clean up its contribution to history on every change, and then become the owner (be on top of the list), acquiring a new position in the history of contributions. |
This started as an attempt to fix #7656, but I now ran into some questions where I'm not sure what's the best answer, to I'm pushing this up as a WIP
The questions:
title
elements at the same time in different components, and the parent title is set toA
, then the child title is set toB
, then the parent title updates its title toA2
. Upon removal, with this PR, it would go back tooA
. Is that fine?