Heterodox view: components are not the primary unit of composition in front end apps.

Components facilitate data flow. Coupling data flow to DOM as a matter of course creates DOM abstractions that are hard to reuse.
Frameworks know this, which is why every framework has a few privileged "directives" (like event handlers) that work with any element and don't use the component abstraction.
Some frameworks provide a nice way to create your own directives (Vue is a good example), but even those frameworks claim that you should think of custom directives as an advanced feature, and that components are the primary form of reuse.
One of the consequences of this is that component APIs frequently have a privileged "main element" concept, and you get more tools for working with that main element (including lifecycle facilities) than other elements. There's no real reason for this.
I don't think Ember Octane really got to the end of this story, but Octane components don't even have a notion of a "main component", let alone a privileged one. You use our version of "directives" (which we call modifiers) to abstract over DOM lifecycle; they work on any element
React Hooks are heading in the right direction here, but the API for abstracting over DOM stuff requires creating a ref and then sticking it on an appropriate element and juggling lifecycle.
Think of Ember modifiers as a dedicated ref syntax that you can stick on any element. The modifier is created (with the element) when the element is created, and its teardown is invoked when the element is invoked.

You don't create or manage refs.
The modifier's syntax is on the element, so that's how it gets associated with the element.

Here's the Ember equivalent of a React on-resize hook.
Don't focus on the separate template file. We agree it's not ideal and will eventually allow those two files to be one file, perhaps even in the next edition.

Also don't focus on the exact details of where the curlies are located. It takes some getting used to for sure.
The point is that you can very easily abstract away the onResize logic into a modifier, and that once you've done so, the entire API is "stick it on an element".

For reference, here's a pretty good version of a hook API for the same thing.
I think the general philosophy of hooks is heading in the right direction here, but the extra juggling that you have to do to attach the ref (compared to invoking a component) can get cumbersome, especially in larger components.
This may make it seem like writing general-purpose DOM abstractions separate from data flow is begging for extra boilerplate, and might make a person familiar with hooks think that it's worth it if you're writing a library, but not necessarily a good rule of thumb.
Another point worth making about ref w/ hooks: because of the way refs get attached, they don't naturally function as lifecycle managers for DOM elements.
This means that abstractions using useRef are relying on the state eventually settling after render happens enough times and useCallback/useEffect stops running.

This is really not ideal, because it creates tons of nonsense intermediate states.
Again, I think react is heading in the right direction here, but ultimately I believe that we should all be iterating on idiomatic ways to attach lifecycle to arbitrary elements that are *so nice* that we can stop thinking of components as the primary way to abstract DOM behavior
I know Ember's currently approach isn't ideal, but with Octane components, we put our money where our mouth is and completely eliminated the notion of a root element, as well as Component APIs that interact with the DOM.
You abstract DOM behavior in modifiers, and modifiers work on any element, in any component. Event handlers are just built-in modifiers, and the old component lifecycle behavior was recast into a modifier that works on any element. https://github.com/emberjs/ember-render-modifiers
Writing a modifier is pretty ergonomic. Function-based modifiers look a lot like React Hooks, and even support the "returning a function" cleanup API.
This means that you can mix and match modifiers built with any high-level modifier implementation on stable Ember, including experimental approaches, and they all boil down to the same stable primitive. This has been incredible for our own iteration.
Finally, for those not familiar with the Ember ecosystem, Octane is not an experimental approach I'm optimistic about. Octane landed in December 2019, and most apps are writing new code using Octane idioms.

So despite the imperfect API, this approach has worked out well.
I'm excited to keep iterating on these ideas, but I gotta tell you: I don't miss the pre-Octane DOM APIs on Ember's components one bit.

Moving element abstractions away from component invocations was difficult to think through at the time, but boy was it worth it.
You can follow @wycats.
Tip: mention @twtextapp on a Twitter thread with the keyword “unroll” to get a link to it.

Latest Threads Unrolled:

By continuing to use the site, you are consenting to the use of cookies as explained in our Cookie Policy to improve your experience.