DOM Element recycling will be removed in Preact 8.
This should have no effect on most applications. In fact, it fixes a number of known issues related to element state not being fully reset on reuse.
💡 Why? Many DOM properties are stateful, and trying to reset them all when recycling is a game of whack-a-mole. Preact's size makes it infeasible to use whitelists to address these issues, so recycling is being dropped.
Recycling as a performance optimization became less relevant in Preact 4 when Component recycling was modified to account for the majority of the cases where Element recycling had previously been relied on. The Pure Function Components rewrite removed the last meaningful use-case for Element recycling, so we're dropping it and the bugs it brought along with it.
The following will stop working in Preact 8:
<div class={{ foo: true }} />
<div className={{ [style.foo]: 0 }} />
💡 Why? Serializing objects prevented CSS solutions from using
toString
casting.
Install and use classnames. It supports this syntax & more and is around 500 bytes.
import cx from 'classnames';
<div class={cx({ foo: true })} /> // nearly the same
<div className={cx([{ foo: true }, 0, 'test'])} /> // nice
If you're fine with polyfilling, here you go:
import { options } from 'preact';
import cx from 'classnames';
let old = options.vnode;
options.vnode = vnode => {
let props = vnode.attributes;
if (props && props.class && typeof props.class==='object') {
props.class = cx(props.class);
}
if (old) old(vnode);
};
Pure Functional Components get backing instances in Preact 8. Outwardly, this means ref
s on PFC's now point to their backing instance instead of their DOM element.
This means instead of being called inline during rendering, they are persisted in the component tree just like Component instances. In fact, they are Component instances. This is pretty nifty, since it allows us to explore new things like Stateful Functional Components and even shouldComponentUpdate
optimization of Pure Functional Components.
You should avoid using ref
on Pure Functional Components. This behavior is Preact-specific and not part of the officially supported API. That said, if you were previously relying on ref
pointing at the rendered DOM element from a Pure Functional Component, simply look for .base
as you would for a Component
instance:
// before:
<PureInput ref={ c => c.focus() } />
// after:
<PureInput ref={ c => c.base.focus() } />
This one's a little trickier:
import { options } from 'preact';
let old = options.vnode;
options.vnode = vnode => {
let f = vnode.nodeName,
props = vnode.attributes,
ref = props && props.ref;
if (typeof f==='function' && !(f.prototype && f.prototype.render) && ref) {
props.ref = c => ref(c.base);
}
if (old) old(vnode);
};
💡 Why? Preact outgrew its simple method for handling functional components thanks to optimizations in 5.x through 7.x. Rather than penalize developers for choosing the lovely Pure Functional Component approach, we're making them just as good as
Component
.This change also enables proper component recycling and inter-component diffing for Functional Components, and considerably simplifies the rendering process. Where previously all Component tests needed to test both
Component
and PFC's, now onlyComponent
need be tested as they share an implementation.
Component.prototype.linkState()
is being removed from Preact itself, since it was only used by a fraction of people. Instead, it is now available as a standalone linkstate module.
The linkstate
module includes a polyfill (linkstate/polyfill
) that installs itself into Preact's Component
. This provides the behavior that was present in 7.x and prior.
I'm upgrading an application from preact 7 to 8 right now, and wanted to add this tidbit to help others:
If you're upgrading to preact 8 and you also use preact-jsx-chai, you may run into a test failure like the one in this issue: developit/preact-jsx-chai#46. To fix it, you'll also need to upgrade
preact-render-to-string
(to 3.6.2 or later).