Composition Order and the Dispose Chain
withMixins states the rule for composition order: mixins are applied right to left, so withMixins(withXxx, withYyy, withZzz, Base) is equivalent to withXxx(withYyy(withZzz(Base))) — withZzz is applied first (its generated class sits closest to Base), and withXxx is applied last (its generated class sits closest to the final, most-derived instance).
That fact has a direct, practical consequence for dispose() that's worth spelling out explicitly, because it's easy to compose mixins correctly and still get cleanup ordering wrong without realizing it.
Working Out the Order
Every mixin covered so far follows the same shape for dispose():
dispose() {
// 1. this mixin's own cleanup runs first
this[managedResource].forEach(r => r.destroy())
// 2. then it hands off to whatever's beneath it
super.dispose()
}
Given withMixins(withXxx, withYyy, withZzz, Base), the resulting prototype chain — from the actual instance outward toward Base — is:
xxxDerived → yyyDerived → zzzDerived → Base (Component)
JavaScript method resolution always starts at the most-derived class. So calling instance.dispose() runs in this order:
xxxDerived's own cleanup code runs first (everything in itsdispose()before thesuper.dispose()call)- It calls
super.dispose(), which runsyyyDerived's own cleanup - Which calls
super.dispose(), runningzzzDerived's own cleanup - Which finally calls
super.dispose(), reachingComponent's own basedispose()(removing the element from the DOM, disposing the underlyingStore) last
In short: the mixin listed first in withMixins(...) has its cleanup logic run first; Component's own base disposal always runs last, regardless of how many mixins sit in between.
Why This Matters
Most of the time, mixin cleanup routines are independent of each other — withForm's form teardown doesn't care whether withA11y's focus trap has already been deactivated. When that's true, order is invisible and nothing here needs any attention.
It stops being invisible the moment one mixin's cleanup logic depends on something another mixin manages. For example, imagine a mixin whose dispose() needs to read a DOM attribute that a differently-ordered mixin's own dispose() has already removed by the time it runs — the outcome now silently depends on which order the two were passed to withMixins, and swapping that order changes behavior with no type error and no warning.
If you're authoring a mixin whose cleanup interacts with state another mixin might also be managing, document that dependency explicitly (in a code comment at minimum, in the README ideally) rather than leaving it to be discovered when someone composes mixins in an order that happens not to work.
A Note on Composing Many Mixins
Each mixin in a chain adds one more constructor frame (via super(...args)) and one more link in the dispose() chain. For the handful of mixins a typical component realistically combines — accessibility, one or two resource-creating mixins, maybe a form — this overhead is negligible. It's worth knowing about mainly so that an unusually deep composition chain (many mixins stacked on a single class) doesn't come as a surprise if you're ever profiling construction or teardown cost; the depth of the withMixins(...) argument list is a direct, visible proxy for how many constructor/dispose frames a given component pays for on every instantiation and disposal.