-
-
Notifications
You must be signed in to change notification settings - Fork 924
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
Fix docs bug, advise against reusing vnode.attrs
itself
#2250
Conversation
Yeah, I think those clarifications in the documentation are a good addition. I'd offer more feedback on the writing (I think it could be edited down a bit) but work is so busy I'm not going to have much time to help out until next month. |
Thanks for plowing on with this Isiah. I've got a few issues with this:
|
here's an augmented version of the flems in #1986 that demonstrates the gotcha. not sure if that's easier to grok as an example? |
The problem cases are mysterious, contrived, difficult to illustrate without the reader first swallowing a fairly involved proposition of intent. But the scenario Isiah's used doesn't actually pose any problems. We're told unspecified unexpected behaviour will occur, but put into practice the sample code seems fine to me (ignoring for a second the growing list bug and the fact oncreate needs to specify a delay or DOM query before mutating the DOM to avoid its instructions happening in the same tick as initial render). |
@cavemansspa can you remember the real world difficulty that prompted the simplified flems you created? |
@barneycarroll -- in practice, i tend to avoid passing attrs through like that, so have not really experienced it directly. i was following the issue and put that together so that i understood it better and where it was stemming from. it's one of those cases of: "it's not really a bug, however, it's not the behavior you'd expect, but it makes sense once you understand what's occurring". |
aace9ef
to
9dd5533
Compare
@barneycarroll @cavemansspa I killed the "Avoid restrictive interfaces", offering it as an implied option instead of an explicit suggestion. Here's how it currently reads, since the diff is fairly unrepresentative of the real semantic change here. (And of course, here's the section I ripped out.) |
Mithril shouldn't be that opinionated anyways, so I'm fine ripping it out. |
@isiahmeadows -- how about combining the code block and some inline comments to highlight the issue a little better? This component implementation and use below demonstrates where you may unexpectedly get lifecycle methods invoked twice.
|
9dd5533
to
ac62bc3
Compare
docs/components.md
Outdated
vnode.dom.classList.add("fade-in") | ||
}, | ||
onbeforeremove: function(vnode) { | ||
// This gets called twice, both times awaited! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe add
Once for the component itself, once for the button `FlexibleButton` which uses the same `attrs`.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably a good idea to add that "why". Will fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You would still have to explain why this would be a problem, a question to which there is no answer.
docs/components.md
Outdated
] | ||
``` | ||
|
||
Instead, what you should do in these cases is use a dedicated attribute like `attrs`. So the above two problematic examples should be fixed to something like these three: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only see one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be an artifact of a previous version. I'll fix it hopefully this weekend (if things go according to plan).
made some comments |
Sorry, I think my message got lost in the noise. The example provided is bad because it doesn't actually produce the bug it claims to illustrate, while providing distracting examples of an involved transition mechanism which doesn't work (and would need to be more involved in order to work). Here's a version with fixed transition logic (terminal / resting states, delay between render pass and oncreate DOM manipulation). As you can see, this doesn't produce the bug it's intended to - attribute forwarding is actually fine in this scenario. @cavemansspa @spacejack I thought we had an open bug for the render -> oncreate delay expectations - did we decide against? |
I'm not sure what you mean by delay expectations - if you mean the "readyness" of the DOM to apply transitions I think the decision was against since userland solutions can be simpler than a general purpose one in the core. (Your flems url is broken BTW.) |
@barneycarroll -- are your referring to #1779 discussion a while back for proposing |
@barneycarroll I didn't test either example, so it's possible they're wrong. My goal is to produce in both cases a bug repro that's 1. reproducible, 2. easy to explain, and 3. relatable in the real world. I might just end up copying and reducing #1775's repro for the attrs issue, but I still need to test the key behavior to have a verified buggy example. (I might just recast that section to reuse the same component between both and rework the first to also provide buggy behavior for the second.) I do plan on revisiting this over the weekend (once I have some more free time), so I'll try to fold some of these suggestions in then. |
OK, never mind the Let's put it another way: we're currently describing this as a footgun for what would appear as bugs, ie the library behaving in unexpected ways, but this is a category error: so far nobody has been able to come up with a tangible problem case. @isiahmeadows' example doesn't illustrate a problem: users could take that pattern and run with it and would never run into any problems; @cavemansspa's example does illustrate the potential for expectation shortfall but it's so abstract that it doesn't really prove anything — if we were to use our imaginations to flesh it out into something with discernible intent (while still illustrating some kind of problem), it would likely be a case of using lifecycle hooks for non-idempotent application logic — which would be a case of relying on Mithril redraw engine logic for side effects — a blatant anti-pattern. My point is that there's no way of presenting this as an implementation detail gotcha without producing a very elaborate problem case which would have to be a fundamentally bad idea of itself. In fact, the real problem with attribute-forwarding is a more subtle one of extensible component interface design, when authors are tempted to get the best of both worlds from a single object: a custom interface that addresses certain context-specific concerns, and the underlying 'generic' interface. Initial convenience leads to intractable ambiguity down the line. This is a perennial problem in AOP interface design most redolent of the ambiguities of subclassing (Should I call super when I override this method? Should I call it before or after my subclassed behaviour?). In DOM component design the problem comes when you want to provide a mix of a custom component attribute interface while leaving the door open for generic DOM attribute customisation: the classic example is some kind of And the solution to that is simply never to forward the attributes object or the filtered remains thereof to a descendant node: where this is desirable the interface author should earmark specific custom attributes (in this case It's a code smell, not a technical gotcha footgun. TL;DR: it's complex, subtle & involved, and no explanation is IMO better than a confusing gesture towards the problem domain. In the absence of an explanation which can clearly illustrate tangible problems in less than 500 words + 100 LoC I vote we just nerf this section of the docs until we come up with something better. |
@barneycarroll I'd still rather find a case where it introduces bugs in user code, and the two linked bugs I'm resolving with this PR are my main driving reason for including this.
I'll try to find a better scenario that correctly runs into issues here once I find time.
BTW, not everyone agrees with your assessment that attribute forwarding is itself inherently bad - @tivac and I are a "sometimes", and @lhorie, who wrote the section, was a "whenever possible". If we've got three different, common viewpoints on the issue and nobody agrees on any of it, I find it better to just rip out the controversial "advice" altogether, without replacement. It's also why I phrased it as an "if" when explaining attribute forwarding, with no implication on whether you should.
This is part of my difficulty in coming up with something "that's 1. reproducible, 2. easy to explain, and 3. relatable in the real world".
I'm good with this as a first step, and then I'll rebase this PR. Edit: #2265 |
See discussion in MithrilJS#2250 and MithrilJS#1986 for more details.
See discussion in MithrilJS#2250 and MithrilJS#1986 for more details.
vnode.attrs
itselfvnode.attrs
itself (don't merge)
I'm putting this on hold until #2262 gets resolved, since this doesn't really make nearly as much sense without that. |
@CreaturesInUnitards Does this intersect with #2294? |
@isiahmeadows sort of — all the stuff about forwarding |
aaf7d51
to
7111231
Compare
Okay, I recast the section entirely with a different example (in this case, a case of third-party integration gone wrong). I also removed the part about keys, and just strictly focused on lifecycle methods and touched a little on component design. It's a little large, but that's because I went with a full Bootstrap modal component and dissected the two main problems within it. |
Just my 2 cents: one thing that jumps out to me in these code examples is the array notation for children. I think it adds visual noise and confusion. It wouldn't surprise me to find out that I'm in the minority, but FWIW if if were up to me we would only recommend and/or demonstrate square brackets as the root of a view. |
@CreaturesInUnitards I'm a creature of habit, but there's a few other reasons I did this:
We can change the docs to prefer variadic parameters, but I'd prefer to keep that discussion in its own issue. * Except when the children is a single |
vnode.attrs
itself (don't merge)vnode.attrs
itself
6ffcb0f
to
e164c6e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The example you use is pretty complex. Couldn't we just do a simple example like this?
@StephanHoyer The reason I redid it was because @barneycarroll wanted a concrete case of where it breaks things. Pretty much everyone who ran into bugs doing this had a layer of abstraction that broke elsewhere, so I had to create that layer of abstraction and indirection to show where it goes wrong. The example also touched on idiomatic API design. I'll kill the header/body/footer attrs customization, which should help simplify it some. Those aren't critical to explain this. |
e164c6e
to
8b0406e
Compare
@StephanHoyer I removed code for the unused parameters, if that helps. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The example is still hard to follow for me. Maybe it's because I never used jquery or bootstrap to such an extend.
docs/components.md
Outdated
} | ||
``` | ||
|
||
It's also less flexible. For example, you can't assign custom classes to your `.modal-header` element itself: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's also less flexible. For example, you can't assign custom classes to your `.modal-header` element itself: | |
It's also less flexible. For example, you can't assign a custom id to your `.modal-header` element itself: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The example is still hard to follow for me. Maybe it's because I never used jquery or bootstrap to such an extend.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The example is still hard to follow for me. Maybe it's because I never used jquery or bootstrap to such an extend.
@StephanHoyer That might be it. Do you have any other libraries that might do the trick? I'd be willing to replace this with a generic vanilla modal library (I don't know of any right off) that would do the same. I'm just used to people being familiar with jQuery and at least some of its ecosystem because it powers like 90% of the web (including Wordpress, Drupal, and Rails for core client-side management) and it's about the only library beyond Bootstrap that designers are generally familiar with. |
I only use vanilla DOM with a drag-drop lib. Other than that, I always render stuff myself. I also always never faced that issue, I was never tempted to forwar But feel free to merge as is. |
b3fae4e
to
870e55e
Compare
@StephanHoyer Is this one easier to follow? It still uses jQuery and Bootstrap, but I kept it more focused on Mithril and I slimmed it down to about half the size. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
much better to follow! 👍
Edit: update per current patch.
Description
Basically added a bunch of new docs to clarify 1. how to correctly avoid restrictive interfaces, and 2. why the obvious is also wrong. I added the known issues people had with lifecycle hooks and I touched a little on better component design.
Motivation and Context
Fixes #1775
Fixes #1986
How Has This Been Tested?
N/A - it's docs-only
Types of changes
Checklist:
docs/change-log.md