stasm / innerself

◘ A tiny view + state management solution using innerHTML
ISC License
778 stars 28 forks source link

[Question] Potential PR for DOM node reconciliation/opt-in persistance #29

Closed f1yn closed 5 years ago

f1yn commented 5 years ago

Hey Staś. 😄 I know it's been a while but I've come back with a potential feature and wanted a take on the feature itself, and if it's worth working on upstream or solely working on a derivative repo.

I added a means of reconciling each html call as a function instead of a straight string return. This enabled me to add function parsing with template literal syntax. In essence I was able to create a set of helpful functions that can be passed down from the root component and downwards. Right now that's just forceUpdate and asNode.

asNode lets a developer create a hanging document node, and depending on the opts declare it as a temporal or persistent node. Since the object ref exists within ref, it essentially allows for the persistence of vanilla HTMLElements, allowing for binding, and other stateful operations that will persist between renders, such as event binding.

I have a working prototype (first working draft) done here. I'm thinking of doing some cleanups so it's a bit more on-par with the documentation/code-style of the things here.

That being said, since I've just started looking into it it's got some drawbacks, but since innerself is fairly static by nature it might make sense:

1) So I'm not bashing the DOM up too badly I've went out of my way to make sure I'm using replaceNode instead of direct innerHTML. This might backfire, but my previous ES5 experience makes me think it's a safe bet. 2) While I have a isFirstRender flag, I don't have any isUnmounting flag setup yet, though depending on who you ask and where you look it seems un-needed, especially for fairly static ui.

A CustomEvent could eaisely be implemented for temporal nodes, and for persistant nodes it might just be a matter of detecting if any keys in the prev aren't in the current render anymore, though that's quite some overhead.

3) I'm using a hack to get this working, but since we aren't using a Virtual DOM (and I honestly don't want one either), it does work. Just looking at the code I'm sure you'll see what I mean.


Anyways, this is more of an inquiry as to whether I make these changes in hopes of a upstream PR, or if I should work on a derivative work. More than happy to do either. I'm really happy with this end result, I'm not wanting to recreate the whole VDOM, just a happy medium for simple ui.

f1yn commented 5 years ago

As an aside (also a bragging point for sure), the reconciliation model I've added lets you still nest html statements, but also allow asNode access, so you can get away with maddening things like this:

return html`
    <div>
        Hello world
        <div>
            ${html`
                Cheese Futon
                ${({ asNode, forceUpdate }) => {
                    // persistent node
                    const [ref, div, isFirstRender] = asNode(document.createElement('div'), { key: 'my-unique-key' });

                    if (isFirstRender) {
                        // is first time it renders
                        div.addEventListener('click', () => {
                            console.log('rerender');
                            forceUpdate();
                        });

                        div.innerHTML = `This is a persistent node ${Math.random()}`;
                    }

                    // otherwise return string ref since the Node will persist with it's first set state
                    return ref;
                }}
            `}
        </div>
    </div>
`;

So even when nested, component composition is still a thing 👍

f1yn commented 5 years ago

My fork/repository will remain hosted here if anyone is interested: https://github.com/flynnham/innerself-x