diff --git a/benches/src/util.js b/benches/src/util.js
index 9711bcd157..5eda4f5027 100644
--- a/benches/src/util.js
+++ b/benches/src/util.js
@@ -5,6 +5,12 @@ export { afterFrame };
export const measureName = 'duration';
+const majorTask = () =>
+ new Promise(resolve => {
+ window.addEventListener('message', resolve, { once: true });
+ window.postMessage('major task delay', '*');
+ });
+
let promise = null;
export function afterFrameAsync() {
if (promise === null) {
@@ -19,10 +25,20 @@ export function afterFrameAsync() {
return promise;
}
-export function measureMemory() {
+export async function measureMemory() {
if ('gc' in window && 'memory' in performance) {
// Report results in MBs
+ performance.mark('gc-start');
window.gc();
+ performance.measure('gc', 'gc-start');
+
+ // window.gc synchronously triggers one Major GC. However that MajorGC
+ // asynchronously triggers additional MajorGCs until the
+ // usedJSHeapSizeBefore and usedJSHeapSizeAfter are the same. Here, we'll
+ // wait a moment for some (hopefully all) additional GCs to finish before
+ // measuring the memory.
+ await majorTask();
+ performance.mark('measure-memory');
window.usedJSHeapSize = performance.memory.usedJSHeapSize / 1e6;
} else {
window.usedJSHeapSize = 0;
diff --git a/devtools/src/devtools.js b/devtools/src/devtools.js
index 433bc7699f..b7efe8666f 100644
--- a/devtools/src/devtools.js
+++ b/devtools/src/devtools.js
@@ -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.13.1', options, {
+ window.__PREACT_DEVTOOLS__.attachPreact('10.13.2', options, {
Fragment,
Component
});
diff --git a/hooks/test/browser/useImperativeHandle.test.js b/hooks/test/browser/useImperativeHandle.test.js
index 24b9e13775..ac71065696 100644
--- a/hooks/test/browser/useImperativeHandle.test.js
+++ b/hooks/test/browser/useImperativeHandle.test.js
@@ -180,7 +180,7 @@ describe('useImperativeHandle', () => {
expect(() => render(, scratch)).to.not.throw();
});
- it('should reset ref to null when the component get unmounted', () => {
+ it('should reset ref object to null when the component get unmounted', () => {
let ref,
createHandleSpy = sinon.spy(() => ({ test: () => 'test' }));
@@ -198,4 +198,25 @@ describe('useImperativeHandle', () => {
expect(createHandleSpy).to.have.been.calledOnce;
expect(ref.current).to.equal(null);
});
+
+ it('should reset ref callback to null when the component get unmounted', () => {
+ const ref = sinon.spy();
+ const handle = { test: () => 'test' };
+ const createHandleSpy = sinon.spy(() => handle);
+
+ function Comp() {
+ useImperativeHandle(ref, createHandleSpy, [1]);
+ return
Test
;
+ }
+
+ render(, scratch);
+ expect(createHandleSpy).to.have.been.calledOnce;
+ expect(ref).to.have.been.calledWith(handle);
+
+ ref.resetHistory();
+
+ render(, scratch);
+ expect(createHandleSpy).to.have.been.calledOnce;
+ expect(ref).to.have.been.calledWith(null);
+ });
});
diff --git a/package-lock.json b/package-lock.json
index cea3238fd5..3ed2995121 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "preact",
- "version": "10.13.1",
+ "version": "10.13.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "preact",
- "version": "10.13.1",
+ "version": "10.13.2",
"license": "MIT",
"devDependencies": {
"@actions/github": "^5.0.0",
diff --git a/package.json b/package.json
index 881a5b4998..24096672cc 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "preact",
"amdName": "preact",
- "version": "10.13.1",
+ "version": "10.13.2",
"private": false,
"description": "Fast 3kb React-compatible Virtual DOM library.",
"main": "dist/preact.js",
diff --git a/src/diff/children.js b/src/diff/children.js
index 6487b55b4c..4c388ed6f3 100644
--- a/src/diff/children.js
+++ b/src/diff/children.js
@@ -2,6 +2,7 @@ import { diff, unmount, applyRef } from './index';
import { createVNode, Fragment } from '../create-element';
import { EMPTY_OBJ, EMPTY_ARR } from '../constants';
import { getDomSibling } from '../component';
+import { isArray } from '../util';
/**
* Diff the children of a virtual node
@@ -70,7 +71,7 @@ export function diffChildren(
null,
childVNode
);
- } else if (Array.isArray(childVNode)) {
+ } else if (isArray(childVNode)) {
childVNode = newParentVNode._children[i] = createVNode(
Fragment,
{ children: childVNode },
@@ -265,7 +266,7 @@ function reorderChildren(childVNode, oldDom, parentDom) {
export function toChildArray(children, out) {
out = out || [];
if (children == null || typeof children == 'boolean') {
- } else if (Array.isArray(children)) {
+ } else if (isArray(children)) {
children.some(child => {
toChildArray(child, out);
});
diff --git a/src/diff/index.js b/src/diff/index.js
index b6243a8bef..477663cd9e 100644
--- a/src/diff/index.js
+++ b/src/diff/index.js
@@ -3,7 +3,7 @@ import { Component, getDomSibling } from '../component';
import { Fragment } from '../create-element';
import { diffChildren } from './children';
import { diffProps, setProperty } from './props';
-import { assign, removeNode, slice } from '../util';
+import { assign, isArray, removeNode, slice } from '../util';
import options from '../options';
/**
@@ -232,7 +232,7 @@ export function diff(
diffChildren(
parentDom,
- Array.isArray(renderResult) ? renderResult : [renderResult],
+ isArray(renderResult) ? renderResult : [renderResult],
newVNode,
oldVNode,
globalContext,
@@ -438,7 +438,7 @@ function diffElementNodes(
i = newVNode.props.children;
diffChildren(
dom,
- Array.isArray(i) ? i : [i],
+ isArray(i) ? i : [i],
newVNode,
oldVNode,
globalContext,
diff --git a/src/diff/props.js b/src/diff/props.js
index 3a589564ea..b0f2f9ee39 100644
--- a/src/diff/props.js
+++ b/src/diff/props.js
@@ -126,16 +126,16 @@ export function setProperty(dom, name, value, oldValue, isSvg) {
} catch (e) {}
}
- // ARIA-attributes have a different notion of boolean values.
- // The value `false` is different from the attribute not
- // existing on the DOM, so we can't remove it. For non-boolean
- // ARIA-attributes we could treat false as a removal, but the
- // amount of exceptions would cost us too many bytes. On top of
- // that other VDOM frameworks also always stringify `false`.
+ // aria- and data- attributes have no boolean representation.
+ // A `false` value is different from the attribute not being
+ // present, so we can't remove it. For non-boolean aria
+ // attributes we could treat false as a removal, but the
+ // amount of exceptions would cost too many bytes. On top of
+ // that other frameworks generally stringify `false`.
if (typeof value === 'function') {
// never serialize functions as attribute values
- } else if (value != null && (value !== false || name.indexOf('-') != -1)) {
+ } else if (value != null && (value !== false || name[4] === '-')) {
dom.setAttribute(name, value);
} else {
dom.removeAttribute(name);
diff --git a/src/util.js b/src/util.js
index 0e9b3b1e56..1c4b499bff 100644
--- a/src/util.js
+++ b/src/util.js
@@ -1,5 +1,7 @@
import { EMPTY_ARR } from './constants';
+export const isArray = Array.isArray;
+
/**
* Assign properties from `props` to `obj`
* @template O, P The obj and props types