snabbdom / snabbdom

A virtual DOM library with focus on simplicity, modularity, powerful features and performance.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Attribute behavior should be improved

bgradin opened this issue · comments

TL;DR: The module system in Snabbdom provides a powerful way to extend the core functionality, but it leads to conventions for vnode attributes that, as a newcomer to this project, I found counterintuitive and would like to improve.

Problem statement

For example, whereas with React.createElement, you would write something like createElement("a", { href: "/" }, "Home"), Snabbdom requires you to wrap attributes in an object called attrs so they can be handled by the attributes module: h("a", { attrs: { href: "/" } }, "Home"). I found this particularly unexpected when using JSX - a JSX snippet of <a href="/">Home</a> will create an HTML element without an href: <a>Home</a>!

Proposal

I think the crux of the issue is that Snabbdom does not provide a mechanism for modules to specify a limited set of keys they will use to access data on a vnode. If a comprehensive list of these keys was maintained internally, any additional keys in the vnode data could be treated as attributes, rather than being disregarded. I think it would make the most sense to make this update in the h function, so both JS and JSX syntax would reap the benefits.

My preference would be for this to be the default functionality, but I suppose this might require a new major or at least minor release. If the maintenance team prefers, it should be possible to gate this functionality behind a configuration option in the init method to preserve backward compatibility.

It's worth pointing out that other projects like herp-inc/snabbdom-jsx provide functionality similar to what I'm describing. However, my point is the behavior of attributes is worth improving within this project, either by default or as a configuration option.

Barring this update, the readme should at least be updated to explicitly call out that JSX syntax still requires usage of the attributes module to assign attributes on elements.

Conclusion

I would appreciate feedback from a maintainer on these ideas. If an agreement is reached, I would be happy to create a pull request. Thanks for your time!

Besides the attrs, there is also the dataset:

snabbdom/test/unit/core.ts

Lines 616 to 619 in 2e6ef1e

assert.deepEqual(toVNode(bothAttrsAndDatasets).data, {
attrs: { foo: "bar" },
dataset: { foo: "bar", again: "again", fooBarBaz: "fbb" }
});

Hi @bgradin. Thanks for sharing your thoughts. I definitely see where you're coming from. I like the separation between modules and think that one can get used to writing attrs. But what you're proposing definitely has advantages in terms of ergonomics.

I think the crux of the issue is that Snabbdom does not provide a mechanism for modules to specify a limited set of keys they will use to access data on a vnode.

Yes, that seems spot on. They do in practice operate on a limited set of keys (most just a single key) but this information is implicit in the code.

Here's an approach that anyone could use today to achieve the desired goal: Initialize Snabbdom with a module that handles any properties at the root of the data object as attributes unless they're used by other modules. To do this, the module could manually be told about the properties that it should ignore. Something like this:

const patch = init([
  classModule, // makes it easy to toggle classes
  propsModule, // for setting properties on DOM elements
  styleModule, // handles styling on elements with support for animations
  rootAttrsModule(new Set(["class", "props", "style"])) // it should also ignore built-in props like `hooks` an `key`
]);

The idea here is that rootAttrsModule ignores all properties in the set that it is given and handles all other properties as attributes. Of course this is a bit hacky as passing this list is a bit ad-hoc. Furthermore, for some modules (at least the thunks module, the properties they use internally is not documented. That being said, I think the approach would work just fine and could be a good way to achieve your goal and get some experience with how it pans out.

To make the above approach not hacky, each module could expose the properties that they operate on and the init function could support a special optional "fallback" module that is told about the properties used by all other modules and which is then allowed to handle all the remaining properties.

With that approach we wouldn't impose any "default" but would preserve the modular nature of Snabbdom.

Curious to hear you thoughts. If others are interested in this feature please do weigh in 😄

Thanks for the feedback @paldepind ! I love the idea of keeping the fallback behavior as an opt-in module. I'll work on a PR for that.

Howdy. I wrote a plugin a while ago to tune the snabbdom jsx signature and allow any non flagged prop to fallback as an attribute. I haven't tested it with thunks but it might work that way too. I haven't gone as far as experimenting with modules (admittedly I wrote it for a separate tool and didn't get much further than that).

https://github.com/geotrev/snabbdom-transform-jsx-props

Edit: oh I see others have definitely already done this lol well if snabbdom can integrate something like this it'll be a happy day!

Edit 2: I went ahead and converted my package to a proper snabbdom module as of v0.3.0.