Skip to content

Commit

Permalink
Merge branch 'main' into add-is-connected
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock authored Mar 25, 2024
2 parents 5a86be1 + d3d57db commit 4d49641
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 56 deletions.
2 changes: 1 addition & 1 deletion devtools/src/devtools.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { options, Fragment, Component } from 'preact';

export function initDevTools() {
if (typeof window != 'undefined' && window.__PREACT_DEVTOOLS__) {
window.__PREACT_DEVTOOLS__.attachPreact('10.20.0', options, {
window.__PREACT_DEVTOOLS__.attachPreact('10.20.1', options, {
Fragment,
Component
});
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "preact",
"amdName": "preact",
"version": "10.20.0",
"version": "10.20.1",
"private": false,
"description": "Fast 3kb React-compatible Virtual DOM library.",
"main": "dist/preact.js",
Expand Down
7 changes: 3 additions & 4 deletions src/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,21 +123,20 @@ export function getDomSibling(vnode, childIndex) {
function renderComponent(component) {
let oldVNode = component._vnode,
oldDom = oldVNode._dom,
parentDom = component._parentDom,
commitQueue = [],
refQueue = [];

if (parentDom) {
if (component._parentDom) {
const newVNode = assign({}, oldVNode);
newVNode._original = oldVNode._original + 1;
if (options.vnode) options.vnode(newVNode);

diff(
parentDom,
component._parentDom,
newVNode,
oldVNode,
component._globalContext,
parentDom.ownerSVGElement !== undefined,
component._parentDom.ownerSVGElement !== undefined,
oldVNode._flags & MODE_HYDRATE ? [oldDom] : null,
commitQueue,
oldDom == null ? getDomSibling(oldVNode) : oldDom,
Expand Down
5 changes: 2 additions & 3 deletions src/diff/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,6 @@ export function unmount(vnode, parentVNode, skipRemove) {
}

r.base = r._parentDom = null;
vnode._component = undefined;
}

if ((r = vnode._children)) {
Expand All @@ -588,7 +587,7 @@ export function unmount(vnode, parentVNode, skipRemove) {
unmount(
r[i],
parentVNode,
skipRemove || typeof vnode.type !== 'function'
skipRemove || typeof vnode.type != 'function'
);
}
}
Expand All @@ -600,7 +599,7 @@ export function unmount(vnode, parentVNode, skipRemove) {

// Must be set to `undefined` to properly clean up `_nextDom`
// for which `null` is a valid value. See comment in `create-element.js`
vnode._parent = vnode._dom = vnode._nextDom = undefined;
vnode._component = vnode._parent = vnode._dom = vnode._nextDom = undefined;
}

/** The `.render()` method for a PFC backing instance. */
Expand Down
103 changes: 58 additions & 45 deletions src/diff/props.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ function setStyle(style, key, value) {
}
}

// A logical clock to solve issues like https://github.com/preactjs/preact/issues/3927.
// When the DOM performs an event it leaves micro-ticks in between bubbling up which means that
// an event can trigger on a newly reated DOM-node while the event bubbles up.
//
// Originally inspired by Vue
// (https://github.com/vuejs/core/blob/caeb8a68811a1b0f79/packages/runtime-dom/src/modules/events.ts#L90-L101),
// but modified to use a logical clock instead of Date.now() in case event handlers get attached
// and events get dispatched during the same millisecond.
//
// The clock is incremented after each new event dispatch. This allows 1 000 000 new events
// per second for over 280 years before the value reaches Number.MAX_SAFE_INTEGER (2**53 - 1).
let eventClock = 0;

/**
* Set a property value on a DOM node
* @param {PreactElement} dom The DOM node to modify
Expand Down Expand Up @@ -68,15 +81,21 @@ export function setProperty(dom, name, value, oldValue, isSvg) {

if (value) {
if (!oldValue) {
value._attached = Date.now();
const handler = useCapture ? eventProxyCapture : eventProxy;
dom.addEventListener(name, handler, useCapture);
value._attached = eventClock;
dom.addEventListener(
name,
useCapture ? eventProxyCapture : eventProxy,
useCapture
);
} else {
value._attached = oldValue._attached;
}
} else {
const handler = useCapture ? eventProxyCapture : eventProxy;
dom.removeEventListener(name, handler, useCapture);
dom.removeEventListener(
name,
useCapture ? eventProxyCapture : eventProxy,
useCapture
);
}
} else {
if (isSvg) {
Expand All @@ -85,18 +104,18 @@ export function setProperty(dom, name, value, oldValue, isSvg) {
// - className --> class
name = name.replace(/xlink(H|:h)/, 'h').replace(/sName$/, 's');
} else if (
name !== 'width' &&
name !== 'height' &&
name !== 'href' &&
name !== 'list' &&
name !== 'form' &&
name != 'width' &&
name != 'height' &&
name != 'href' &&
name != 'list' &&
name != 'form' &&
// Default value in browsers is `-1` and an empty string is
// cast to `0` instead
name !== 'tabIndex' &&
name !== 'download' &&
name !== 'rowSpan' &&
name !== 'colSpan' &&
name !== 'role' &&
name != 'tabIndex' &&
name != 'download' &&
name != 'rowSpan' &&
name != 'colSpan' &&
name != 'role' &&
name in dom
) {
try {
Expand Down Expand Up @@ -124,38 +143,32 @@ export function setProperty(dom, name, value, oldValue, isSvg) {
}

/**
* Proxy an event to hooked event handlers
* @param {PreactEvent} e The event object from the browser
* Create an event proxy function.
* @param {boolean} useCapture Is the event handler for the capture phase.
* @private
*/
function eventProxy(e) {
if (this._listeners) {
const eventHandler = this._listeners[e.type + false];
/**
* This trick is inspired by Vue https://github.com/vuejs/core/blob/main/packages/runtime-dom/src/modules/events.ts#L90-L101
* when the dom performs an event it leaves micro-ticks in between bubbling up which means that an event can trigger on a newly
* created DOM-node while the event bubbles up, this can cause quirky behavior as seen in https://github.com/preactjs/preact/issues/3927
*/
if (!e._dispatched) {
// When an event has no _dispatched we know this is the first event-target in the chain
// so we set the initial dispatched time.
e._dispatched = Date.now();
// When the _dispatched is smaller than the time when the targetted event handler was attached
// we know we have bubbled up to an element that was added during patching the dom.
} else if (e._dispatched <= eventHandler._attached) {
return;
function createEventProxy(useCapture) {
/**
* Proxy an event to hooked event handlers
* @param {PreactEvent} e The event object from the browser
* @private
*/
return function (e) {
if (this._listeners) {
const eventHandler = this._listeners[e.type + useCapture];
if (e._dispatched == null) {
e._dispatched = eventClock++;

// When `e._dispatched` is smaller than the time when the targeted event
// handler was attached we know we have bubbled up to an element that was added
// during patching the DOM.
} else if (e._dispatched < eventHandler._attached) {
return;
}
return eventHandler(options.event ? options.event(e) : e);
}
return eventHandler(options.event ? options.event(e) : e);
}
};
}

/**
* Proxy an event to hooked event handlers
* @param {PreactEvent} e The event object from the browser
* @private
*/
function eventProxyCapture(e) {
if (this._listeners) {
return this._listeners[e.type + true](options.event ? options.event(e) : e);
}
}
const eventProxy = createEventProxy(false);
const eventProxyCapture = createEventProxy(true);

0 comments on commit 4d49641

Please sign in to comment.