Skip to content
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

Remount component on parameter change #148

Closed
ganigeorgiev opened this issue Oct 11, 2020 · 8 comments
Closed

Remount component on parameter change #148

ganigeorgiev opened this issue Oct 11, 2020 · 8 comments
Labels
question Further information is requested

Comments

@ganigeorgiev
Copy link

ganigeorgiev commented Oct 11, 2020

Consider the following route definition:

'/users/:id': UserView

Navigating from /users/1 to /users/2 will not remount the example UserView component and therefore any actions (eg. fetching the targeted user) binded to onMount are not triggered.

This seems to be intended (probably #L476) but what it is the reason behind that? Is it possible to add an optional config to force it?


ps. I've just checked other common js routers - it seems that they follow similar approach, but in my opinion having to "watch" the router parameters adds unnecessary boilerplate and overall it's not the same as remounting/reinstantiating.

@ItalyPaleAle
Copy link
Owner

It is done on purpose indeed, for the best user and developer experience :)

What you can do is to watch for changes to params.id and reactively update your component.

You can see an example here: https://github.com/ItalyPaleAle/book-app-demo/blob/master/src/components/Book.svelte

@ItalyPaleAle ItalyPaleAle added the question Further information is requested label Oct 11, 2020
@ganigeorgiev
Copy link
Author

ganigeorgiev commented Oct 11, 2020

@ItalyPaleAle as I noted in the ps., watching the parameter it's not the same as remounting the component and it's not always trivial to handle for example transitions and other layout changes.

@ItalyPaleAle
Copy link
Owner

It's not, but when working with Svelte it's best practice to minimize the amount of DOM changes. In fact, the Svelte compiler itself goes great length to ensure that!

If you really want to re-mount the component, assuming your route is in the User component.

  1. Place your view in a UserInner component.
  2. Change your User component to include UserInner, then watch for changes to params.id to re-mount it

This is User (note: haven't tested it):

<!-- Show the UserInner component (with the same props as this route) when load is truth-y -->
{#if load}
    <UserInner {props} />
{/if}

<script>
// Import the UserInner component
import UserInner from './UserInner.svelte'

// Import the tick method
import {tick} from 'svelte'

// Props passed by the router
export let props = {}

// When this is true, show the component
let load = false

// When props.id changes, then trigger loadComponent
$: loadComponent(props.id)
let running = false
async function loadComponent() {
    // Semaphore for concurrency
    if (running) {
        return
    }
    running = true

    // Un-mount the component
    load = false

    // Wait for the next tick
    await tick()

    // Re-mount the component
    load = true
    running = false
}
</script>

The important thing is the await tick(), because you need to give time to Svelte to un-mount your component from the DOM. As I said, Svelte really doesn't like large DOM changes, and you need to fight for it :)

@ganigeorgiev
Copy link
Author

@ItalyPaleAle Thanks for the suggestion. Actually I'm currently doing something very similar, but decided to go with {#key expression}...{/key} in the template instead of if.

@ItalyPaleAle
Copy link
Owner

That works too. But you need to make sure expression isn't just true/false because if you go from true->false->true in the same tick of the event loop, I don't believe anything changes. So you can use props.id as expression in key I believe (haven't tried it)

@vkurko
Copy link

vkurko commented Jul 2, 2021

It is done on purpose indeed, for the best user and developer experience :)

What you can do is to watch for changes to params.id and reactively update your component.

You can see an example here: https://github.com/ItalyPaleAle/book-app-demo/blob/master/src/components/Book.svelte

Hi @ItalyPaleAle

First of all let me thank you for the great router you have created. It is really cool and works just fine!

However, I agree with @ganigeorgiev (and I'm sure many others) that having the ability to remount a component when a parameter is changed will greatly improve the development experience in some cases. Imagine a complex application with transitions, Svelte actions, multiple nested components, stores with default values, and HTTP requests to initialize stores. It may be impossible or too expensive to revert an application to its original state just by monitoring the props and reactively updating the component.

Your solution with UserInner does the trick. But it would be really nice to have a built-in option to enable this feature in your router.

Please consider implementing such an option.

Thanks

@ItalyPaleAle
Copy link
Owner

@vkurko While I understand your point, it would be too much of a breaking change right now as I'm sure lots of people depend on this behavior (see Hyrum's law). The workaround of having an "inner" component is still what I'd recommend in this case and hopefully it's not too much of a hassle to implement.

@vkurko
Copy link

vkurko commented Jul 4, 2021

@ItalyPaleAle, thanks for the answer. But you shouldn't break existing behavior! What I (and others) ask for is simply the ability to switch existing behavior to "remount" the component. This setting can be turned off by default, so this change will not affect anyone. But users who need this "remount" can enable it in their projects.

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants