jorgebucaran / superfine

Absolutely minimal view layer for building web interfaces
https://git.io/super
MIT License
1.56k stars 78 forks source link

Element destroyed and recreated when an element is prepended #151

Closed Wildhoney closed 5 years ago

Wildhoney commented 5 years ago

I can't seem to find if this has been addressed before, but I do vaguely remember a discussion somewhere regarding it, so apologies if it's a duplicate.

Using a simple contrived example:

const view = showLabel =>
    h('form', {}, [
        showLabel && h('label', {}, 'Text:'),
        h('input', { type: 'text' })
    ]);

With the above view being called twice — the first time with showLabel to false and the second time to true — the input element is destroyed and recreated, and thus loses focus:

view(false);
view(true);

However if you reverse the two h elements and show the label element last, the input is not destroyed and recreated, and instead reused.

Is it not possible to detect that h('input', { type: 'text' }) is not a new element and so to reuse it? Or is there a good reason as to why it's implemented the way it is?

jorgebucaran commented 5 years ago

@Wildhoney This is a FAQ. You need to key the input text to preserve the element. See keys in the docs.

https://codepen.io/anon/pen/MPQgmg?editors=0110

import { h, patch } from "https://unpkg.com/superfine?module"

const view = showLabel =>
    h('form', {}, [
        showLabel && h('label', {}, 'Text'),
        h('input', { 
          type: 'text', 
          value: 'The key is to use keys!', 
          key: 'label', 
          autofocus: true 
        })
    ]);

const app = (view, container, node) => state => {
  node = patch(node, view(state), container)
}

const render = app(view, document.body)

render(false)

setTimeout(() => render(true), 3000)
leeoniya commented 5 years ago

@jorgebucaran docs section on keys shows an example where siblings are identical tags. this is a case where keying is needed in all libs. it's unusual that superfine requires keying of dissimilar elements to prevent recreation - this is not at all apparent from the example. the docs could use some further clarification of this behavior.

jorgebucaran commented 5 years ago

Keys solve this issue, so I'm fine with this solution.

@leeoniya ...the docs could use some further clarification of this behavior.

Sure, I couldn't agree more.

Wildhoney commented 5 years ago

Sorry, I know the key solves the issue as that's what I've been doing hitherto. That is not the issue, instead it's more philosophical as to the reasoning behind it.

For instance I totally understand why identical elements would need a key, however dissimilar elements I don't quite get, because in my mind it seems perfectly soluble. It starts to make even less sense when append works perfectly well, but a prepend of the same element causes problems.

For example if all of the subsequent elements were elements that we trust are not destroyed-recreated then we'd need to key all of them statistically.

Is it a case that it's computationally expensive to shallowly traverse the current node list to see if the element exists elsewhere, as the prepend has moved it out of its place? If so, does it not make more sense to provide a flag to make this behaviour opt-in, rather than resorting to key.

Is it a case that implementing the behaviour would cause issues elsewhere?

Otherwise it appears that key is being shoehorned in to solve an issue that works by-accident rather than by-design in this instance (although I know that key has perfectly valid applications in other tree snippets – my issue is not with key).

jorgebucaran commented 5 years ago

@Wildhoney

Is it a case that it's computationally expensive to shallowly traverse the current node list to see if the element exists elsewhere, as the prepend has moved it out of its place?

...in my mind it seems perfectly soluble.

I didn't realize. I was never bothered by this, so it was never in my mind to try to "solve" it. Would you like to give it a shot? I have nothing against making Superfine and Hyperapp this way. It sounds like a good idea. Maybe it's not a lot of code either. One can only hope.

Otherwise it appears that key is being shoehorned in to solve an issue that works by-accident rather than by-design...

Acknowledged.

If so, does it not make more sense to provide a flag to make this behaviour opt-in, rather than resorting to key.

Hyperapp, Superfine, etc., don't use opt-in/out flags. That's by design. It's how I choose to design my software. I know it's not perfect. I admit I sacrifice flexibility for elegance / simplicity / ingenuity—call it whatever you want.

There could be various flags and configuration options. Some people like that. I respect them. Some VDOMs out there can even make coffee. That's a joke. No, they can't make coffee, but they have capabilities that I'd consider extraneous even with an open mind. I don't think such software to be poor. Far from that. They are just not designed the way I like. Perhaps that's why I always end up reinventing the wheel with most of my projects.

jorgebucaran commented 5 years ago

Awesome! 🎉

jorgebucaran commented 5 years ago

@frenzzy Why did you delete your comment?

jorgebucaran commented 5 years ago

@leeoniya Please don't use my project's issue tracker to promote your projects. Thanks.

@Wildhoney Let's continue the discussion in #152. 👋


Closing in favor of #152.