-
Notifications
You must be signed in to change notification settings - Fork 58
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
Use data-diffKey to pass list update hints #74
base: master
Are you sure you want to change the base?
Conversation
Thanks for this PR! - I'm not sure this is the right resolution tho. In choo 6 every API we ship is backed by a web standard (or our interpretation of one). This means that the friction between the web platform, and the framework APIs is heavily reduced. The flip side of this is that we can't come up with new APIs, but in a way that's good. I think the problems you outlined can be solved by creating a key generator with a different seed for each list. E.g. something like: var createSeed = require('my-generator')
var seed = createSeed() // create a new ID generator
var id = seed.next() // get a new ID
var id = seed.next()) // get a new ID
var id = seed.next()) // get a new ID I hope it makes sense why I'd favor a different approach than this PR - let me know if you need help with building a seed generator; always happy to help ✨ Cheers! |
@yoshuawuyts Thanks for your note. I'm not quite sure I understand what you mean by adding no APIs. FWIW,
That describes diff algorithm hinting. A PRNG or similar is a fine way to generate names that are unlikely to collide, if you have a good source of seeds that you know are unique. It makes the name picking problem seem smaller---you have to pick unique seeds, not unique ids for every child---but doesn't avoid it. You could do the same with prefixing: The use case I have where this breaks down renders a kind of Merkle tree. Each branch and leaf in the tree comes with a cryptographic hash of its content. The content is some, but not all of the state that needs to be rendered at branches and leaves. Hashes are guaranteed to correspond to content. The same content can appear at multiple positions in the tree. As a result, hashes aren't unique. If the same content recurs within the tree, so will the hash. So setting I suspect other apps dealing with data that's inherently tree-like, with deep nesting, will have the same problem. Those are also use cases where the performance limits of the functional-reactive come up fast. You have to cheat those limits with rerendering and diff optimizations. |
For me this far this was not a problem, as I'm using nanocomponent's implicit ID assignment for unique keys. In my understanding that From an implementation standpoint - in nanomorph - it wouldn't make any difference where it's the ID attribute or the dataset or anything else. I can see this being an issue if you use a different abstraction than nanocomponent, with maybe it's own different concepts. Could you use that scheme for your merkle tree rendering? You would need to create one nano instance for each leaf, even if two leaves share the same content. |
I'm +1 for changing this if we can prove this is not flexible enough with a real world use case, and maybe rendering merkle trees does that. |
@juliangruber, thanks for your messages. I will have to take a look at nanocomponent. Hopefully this evening. |
@juliangruber: Had a look at nanocomponent. Looks like it creates a unique In other words, if The hypothetical app I've been thinking of---not my project, but tree-like---is a Git tree viewer. Two components: Tree and Blob. Tree renders its own data, plus any other Trees and Blobs within it. Will all Trees get the same nanocomponent |
By the way, would one of y'all mind reopening this? Helps me keep track of it. |
Due to DOM limitations, one instance of a component can only be rendered exactly once at a time. If you want to show multiple of a component type, create multiple instances -> they all have their unique IDs.
@yoshuawuyts can |
@juliangruber, ah, I see. That solves the Thinking in terms of a Git tree renderer, with Tree and Blob components. First pass state:
All new elements:
Then a state change adds another tree and blob:
Assuming optimal
Edit: Fix |
I'm going to cook up a little demo app and check. Hopefully the result is what I listed above, and not something like this, which defeats the purpose of the
|
this has more to do with how you manage component instances in JS land I think, because that's really where you ensure same ID gets matched with same ID, ie if you remove a node it gets removed from your array of nodes, etc. |
@juliangruber: Absolutely. I've played around a bit with nanocomponent, and I think I see this clearly from the top down now. If you use nanocomponent in functional-reactive/React style, where you rerender the entire UI tree on each state change, with new-versus-old-state checks like You can cheat this by stepping away from the functional-reactive style in your components, caching elements from the last render pass and using DOM methods to mutate to what you need, based on how state has changed. This approach takes you back toward an older, more stateful component approach, the one a lot of us know from Backbone. Not all the way back, but to a middle ground. Your components become The simple example in nanocomponent's Where does that leave us? I will probably run a patch to nanomorph for my own project, adding the I still think the change is good for nanomorph generally, since folks will be interested to use in the FRP style, even if choo style ends up being more stateful. I'd love to find out how you benchmark choo front-end perf, and try a well-known example with key-based diff hints. I suspect the benefit of better diff optimization would outweigh the cost of an additional property tree-wide in many use cases, but that's the kind of thing you have to try not to think about, and just bench. |
Excellent analysis, @kemitchell! What do you think about making the It's interesting how similar nano seems to react components yet using instances instead of data identifiers shows a huge conceptual difference. I wonder if this is a conscious decision or rather a (happy) accident of design? @yoshuawuyts |
@juliangruber Making the identify key configurable---either |
This tiny PR adds a single
if
statement to thesame(a, b)
function used to find good list-reorder candidates in lists of elements. The new check identifies matches whena.dataset.diffkey === b.dataset.diffkey
.This has the effect of decoupling diff-hint keys from
id
values. A couple nice benefits:No pressure to make diff-hint keys globally unique. For diff optimization, the only constraint is that diff-hint keys should be unique within the element list.
No risk of collision with other users of the
id
namespace, like<a href="#x">jump</a>
targets.I'm not particularly attached to
data-diffkey
as the attribute name, but it's short, self-documenting, and easy to access withelement.dataset.diffkey
.Big thanks to @juliangruber for the recent refactor. Very clear.