Skip to content

Commit 06d2463

Browse files
authored
Merge branch 'master' into master
2 parents 6c78528 + 7c3e6fe commit 06d2463

16 files changed

+560
-57
lines changed

README.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,15 @@ Preact supports modern browsers and IE9+:
6464
#### Real-World Apps
6565

6666
- [**Preact Hacker News**](https://hn.kristoferbaxter.com) _([GitHub Project](https://github.com/kristoferbaxter/preact-hn))_
67-
- [**Play.cash**](https://play.cash) :notes:
67+
- [**Play.cash**](https://play.cash) :notes: _([GitHub Project](https://github.com/feross/play.cash))_
68+
- [**BitMidi**](https://bitmidi.com/) 🎹 Wayback machine for free MIDI files _([GitHub Project](https://github.com/feross/bitmidi.com))_
6869
- [**ESBench**](http://esbench.com) is built using Preact.
6970
- [**BigWebQuiz**](https://bigwebquiz.com) _([GitHub Project](https://github.com/jakearchibald/big-web-quiz))_
7071
- [**Nectarine.rocks**](http://nectarine.rocks) _([GitHub Project](https://github.com/developit/nectarine))_ :peach:
7172
- [**TodoMVC**](https://preact-todomvc.surge.sh) _([GitHub Project](https://github.com/developit/preact-todomvc))_
7273
- [**OSS.Ninja**](https://oss.ninja) _([GitHub Project](https://github.com/developit/oss.ninja))_
7374
- [**GuriVR**](https://gurivr.com) _([GitHub Project](https://github.com/opennewslabs/guri-vr))_
7475
- [**Color Picker**](https://colors.now.sh) _([GitHub Project](https://github.com/lukeed/colors-app))_ :art:
75-
- [**Rainbow Explorer**](https://use-the-platform.com/rainbow-explorer/) _([GitHub Project](https://github.com/vaneenige/rainbow-explorer/))_ :rainbow:
7676
- [**Offline Gallery**](https://use-the-platform.com/offline-gallery/) _([GitHub Project](https://github.com/vaneenige/offline-gallery/))_ :balloon:
7777
- [**Periodic Weather**](https://use-the-platform.com/periodic-weather/) _([GitHub Project](https://github.com/vaneenige/periodic-weather/))_ :sunny:
7878
- [**Rugby News Board**](http://nbrugby.com) _[(GitHub Project)](https://github.com/rugby-board/rugby-board-node)_
@@ -96,7 +96,7 @@ Preact supports modern browsers and IE9+:
9696

9797
- [**Preact Boilerplate**](https://preact-boilerplate.surge.sh) _([GitHub Project](https://github.com/developit/preact-boilerplate))_ :zap:
9898
- [**Preact Offline Starter**](https://preact-starter.now.sh) _([GitHub Project](https://github.com/lukeed/preact-starter))_ :100:
99-
- [**Preact PWA**](https://preact-pwa.appspot.com/) _([GitHub Project](https://github.com/ezekielchentnik/preact-pwa))_ :hamburger:
99+
- [**Preact PWA**](https://preact-pwa-yfxiijbzit.now.sh/) _([GitHub Project](https://github.com/ezekielchentnik/preact-pwa))_ :hamburger:
100100
- [**Parcel + Preact + Unistore Starter**](https://github.com/hwclass/parcel-preact-unistore-starter)
101101
- [**Preact Mobx Starter**](https://awaw00.github.io/preact-mobx-starter/) _([GitHub Project](https://github.com/awaw00/preact-mobx-starter))_ :sunny:
102102
- [**Preact Redux Example**](https://github.com/developit/preact-redux-example) :star:
@@ -138,6 +138,7 @@ Preact supports modern browsers and IE9+:
138138
- :electric_plug: [**preact-routlet**](https://github.com/k1r0s/preact-routlet): Simple `Component Driven` Routing for Preact using ES7 Decorators
139139
- :fax: [**preact-bind-group**](https://github.com/k1r0s/preact-bind-group): Preact Forms made easy, Group Events into a Single Callback
140140
- :hatching_chick: [**preact-habitat**](https://github.com/zouhir/preact-habitat): Declarative Preact widgets renderer in any CMS or DOM host ([demo](https://codepen.io/zouhir/pen/brrOPB)).
141+
- :tada: [**proppy-preact**](https://github.com/fahad19/proppy): Functional props composition for Preact components
141142

142143
#### UI Component Libraries
143144

@@ -250,8 +251,8 @@ import { h, render, Component } from 'preact';
250251

251252
class Clock extends Component {
252253
render() {
253-
let time = new Date().toLocaleTimeString();
254-
return <span>{ time }</span>;
254+
let time = new Date();
255+
return <time datetime={time.toISOString()}>{ time.toLocaleTimeString() }</time>;
255256
}
256257
}
257258

config/rollup.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default {
2727
sourceMap: true,
2828
exclude: 'node_modules/**',
2929
babelrc: false,
30+
comments: false,
3031
presets: [
3132
['env', {
3233
modules: false,

config/rollup.config.esm.js config/rollup.config.module.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import config from './rollup.config';
22

33
// ES output
44
config.output.format = 'es';
5-
config.output.file = 'dist/preact.esm.js';
5+
config.output.file = 'dist/preact.mjs';
66

77
// remove memory() plugin
88
config.plugins.splice(0, 1);

package.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "preact",
3-
"version": "8.2.9",
3+
"version": "8.3.0",
44
"description": "Fast 3kb React alternative with the same modern API. Components & Virtual DOM.",
55
"main": "dist/preact.js",
6-
"jsnext:main": "dist/preact.esm.js",
7-
"module": "dist/preact.esm.js",
6+
"jsnext:main": "dist/preact.mjs",
7+
"module": "dist/preact.mjs",
88
"dev:main": "dist/preact.dev.js",
99
"minified:main": "dist/preact.min.js",
1010
"types": "dist/preact.d.ts",
@@ -16,13 +16,13 @@
1616
"flow": "flow",
1717
"transpile:main": "rollup -c config/rollup.config.js",
1818
"transpile:devtools": "rollup -c config/rollup.config.devtools.js",
19-
"transpile:esm": "rollup -c config/rollup.config.esm.js",
19+
"transpile:esm": "rollup -c config/rollup.config.module.js",
2020
"transpile:debug": "babel debug/ -o debug.js -s",
2121
"transpile": "npm-run-all transpile:main transpile:esm transpile:devtools transpile:debug",
2222
"optimize": "uglifyjs dist/preact.dev.js -c conditionals=false,sequences=false,loops=false,join_vars=false,collapse_vars=false --pure-funcs=Object.defineProperty --mangle-props --mangle-regex=\"/^(_|normalizedNodeName|nextBase|prev[CPS]|_parentC)/\" --name-cache config/properties.json -b width=120,quote_style=3 -o dist/preact.js -p relative --in-source-map dist/preact.dev.js.map --source-map dist/preact.js.map",
2323
"minify": "uglifyjs dist/preact.js -c collapse_vars,evaluate,screw_ie8,unsafe,loops=false,keep_fargs=false,pure_getters,unused,dead_code -m -o dist/preact.min.js -p relative --in-source-map dist/preact.js.map --source-map dist/preact.min.js.map",
2424
"strip:main": "jscodeshift --run-in-band -s -t config/codemod-strip-tdz.js dist/preact.dev.js && jscodeshift --run-in-band -s -t config/codemod-const.js dist/preact.dev.js && jscodeshift --run-in-band -s -t config/codemod-let-name.js dist/preact.dev.js",
25-
"strip:esm": "jscodeshift --run-in-band -s -t config/codemod-strip-tdz.js dist/preact.esm.js && jscodeshift --run-in-band -s -t config/codemod-const.js dist/preact.esm.js && jscodeshift --run-in-band -s -t config/codemod-let-name.js dist/preact.esm.js",
25+
"strip:esm": "jscodeshift --run-in-band -s -t config/codemod-strip-tdz.js dist/preact.mjs && jscodeshift --run-in-band -s -t config/codemod-const.js dist/preact.mjs && jscodeshift --run-in-band -s -t config/codemod-let-name.js dist/preact.mjs",
2626
"strip": "npm-run-all strip:main strip:esm",
2727
"size": "node -e \"process.stdout.write('gzip size: ')\" && gzip-size --raw dist/preact.min.js",
2828
"test": "npm-run-all lint --parallel test:mocha test:karma test:ts test:flow test:size",

src/component.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,18 @@ extend(Component.prototype, {
4646

4747
/**
4848
* Update component state and schedule a re-render.
49-
* @param {object} state A hash of state properties to update with new values
49+
* @param {object} state A dict of state properties to be shallowly merged
50+
* into the current state, or a function that will produce such a dict. The
51+
* function is called with the current state and props.
5052
* @param {() => void} callback A function to be called once component state is
5153
* updated
5254
*/
5355
setState(state, callback) {
54-
let s = this.state;
55-
if (!this.prevState) this.prevState = extend({}, s);
56-
extend(s, typeof state==='function' ? state(s, this.props) : state);
56+
if (!this.prevState) this.prevState = this.state;
57+
this.state = extend(
58+
extend({}, this.state),
59+
typeof state === 'function' ? state(this.state, this.props) : state
60+
);
5761
if (callback) this._renderCallbacks.push(callback);
5862
enqueueRender(this);
5963
},

src/dom/index.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { IS_NON_DIMENSIONAL } from '../constants';
2+
import { applyRef } from '../util';
23
import options from '../options';
34

45
/**
@@ -70,8 +71,8 @@ export function setAccessor(node, name, old, value, isSvg) {
7071
// ignore
7172
}
7273
else if (name==='ref') {
73-
if (old) old(null);
74-
if (value) value(node);
74+
applyRef(old, null);
75+
applyRef(value, node);
7576
}
7677
else if (name==='class' && !isSvg) {
7778
node.className = value || '';

src/preact.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ declare namespace preact {
112112
...children: ComponentChildren[]
113113
): VNode<any>;
114114

115-
function render(node: ComponentChild, parent: Element | Document, mergeWith?: Element): Element;
115+
function render(node: ComponentChild, parent: Element | Document | ShadowRoot | DocumentFragment, mergeWith?: Element): Element;
116116
function rerender(): void;
117117
function cloneElement(element: JSX.Element, props: any): JSX.Element;
118118

@@ -675,6 +675,7 @@ declare global {
675675
optimum?: number;
676676
pattern?: string;
677677
placeholder?: string;
678+
playsInline?: boolean;
678679
poster?: string;
679680
preload?: string;
680681
radioGroup?: string;

src/preact.js

+6
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ import { render } from './render';
55
import { rerender } from './render-queue';
66
import options from './options';
77

8+
function createRef() {
9+
return {};
10+
}
11+
812
export default {
913
h,
1014
createElement,
1115
cloneElement,
16+
createRef,
1217
Component,
1318
render,
1419
rerender,
@@ -19,6 +24,7 @@ export {
1924
h,
2025
createElement,
2126
cloneElement,
27+
createRef,
2228
Component,
2329
render,
2430
rerender,

src/render-queue.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ export function enqueueRender(component) {
2020

2121
/** Rerender all enqueued dirty components */
2222
export function rerender() {
23-
let p, list = items;
24-
items = [];
25-
while ( (p = list.pop()) ) {
23+
let p;
24+
while ( (p = items.pop()) ) {
2625
if (p._dirty) renderComponent(p);
2726
}
2827
}

src/util.js

+11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ export function extend(obj, props) {
1010
return obj;
1111
}
1212

13+
/** Invoke or update a ref, depending on whether it is a function or object ref.
14+
* @param {object|function} [ref=null]
15+
* @param {any} [value]
16+
*/
17+
export function applyRef(ref, value) {
18+
if (ref!=null) {
19+
if (typeof ref=='function') ref(value);
20+
else ref.current = value;
21+
}
22+
}
23+
1324
/**
1425
* Call a function asynchronously, as soon as possible. Makes
1526
* use of HTML Promise to schedule the callback if available,

src/vdom/component-recycler.js

+10-24
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,11 @@
11
import { Component } from '../component';
22

33
/**
4-
* Retains a pool of Components for re-use, keyed on component name.
5-
* Note: since component names are not unique or even necessarily available,
6-
* these are primarily a form of sharding.
7-
* @type {Object.<string, Component[]>}
4+
* Retains a pool of Components for re-use.
5+
* @type {Component[]}
86
* @private
97
*/
10-
const components = {};
11-
12-
13-
/**
14-
* Reclaim a component for later re-use by the recycler.
15-
* @param {Component} component The component to collect
16-
*/
17-
export function collectComponent(component) {
18-
let name = component.constructor.name;
19-
(components[name] || (components[name] = [])).push(component);
20-
}
8+
export const recyclerComponents = [];
219

2210

2311
/**
@@ -29,8 +17,7 @@ export function collectComponent(component) {
2917
* @returns {import('../component').Component}
3018
*/
3119
export function createComponent(Ctor, props, context) {
32-
let list = components[Ctor.name],
33-
inst;
20+
let inst, i = recyclerComponents.length;
3421

3522
if (Ctor.prototype && Ctor.prototype.render) {
3623
inst = new Ctor(props, context);
@@ -43,15 +30,14 @@ export function createComponent(Ctor, props, context) {
4330
}
4431

4532

46-
if (list) {
47-
for (let i=list.length; i--; ) {
48-
if (list[i].constructor===Ctor) {
49-
inst.nextBase = list[i].nextBase;
50-
list.splice(i, 1);
51-
break;
52-
}
33+
while (i--) {
34+
if (recyclerComponents[i].constructor===Ctor) {
35+
inst.nextBase = recyclerComponents[i].nextBase;
36+
recyclerComponents.splice(i, 1);
37+
return inst;
5338
}
5439
}
40+
5541
return inst;
5642
}
5743

src/vdom/component.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { SYNC_RENDER, NO_RENDER, FORCE_RENDER, ASYNC_RENDER, ATTR_KEY } from '../constants';
22
import options from '../options';
3-
import { extend } from '../util';
3+
import { extend, applyRef } from '../util';
44
import { enqueueRender } from '../render-queue';
55
import { areComponentsEqual, getNodeProps } from './index';
66
import { diff, mounts, diffLevel, flushMounts, recollectNodeTree, removeChildren } from './diff';
7-
import { createComponent, collectComponent } from './component-recycler';
7+
import { createComponent, recyclerComponents } from './component-recycler';
88
import { removeNode } from '../dom/index';
99

1010
/**
@@ -52,7 +52,7 @@ export function setComponentProps(component, props, renderMode, context, mountAl
5252
}
5353
}
5454

55-
if (component.__ref) component.__ref(component);
55+
applyRef(component.__ref, component);
5656
}
5757

5858

@@ -84,8 +84,8 @@ export function renderComponent(component, renderMode, mountAll, isChild) {
8484
rendered, inst, cbase;
8585

8686
if (component.constructor.getDerivedStateFromProps) {
87-
previousState = extend({}, previousState);
88-
component.state = extend(state, component.constructor.getDerivedStateFromProps(props, state));
87+
state = extend(extend({}, state), component.constructor.getDerivedStateFromProps(props, state));
88+
component.state = state;
8989
}
9090

9191
// if updating
@@ -287,10 +287,10 @@ export function unmountComponent(component) {
287287
component.nextBase = base;
288288

289289
removeNode(base);
290-
collectComponent(component);
290+
recyclerComponents.push(component);
291291

292292
removeChildren(base);
293293
}
294294

295-
if (component.__ref) component.__ref(null);
295+
applyRef(component.__ref, null);
296296
}

src/vdom/diff.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { buildComponentFromVNode } from './component';
44
import { createNode, setAccessor } from '../dom/index';
55
import { unmountComponent } from './component';
66
import options from '../options';
7+
import { applyRef } from '../util';
78
import { removeNode } from '../dom/index';
89

910
/**
@@ -283,7 +284,7 @@ export function recollectNodeTree(node, unmountOnly) {
283284
else {
284285
// If the node's VNode had a ref function, invoke it with null here.
285286
// (this is part of the React spec, and smart for unsetting references)
286-
if (node[ATTR_KEY]!=null && node[ATTR_KEY].ref) node[ATTR_KEY].ref(null);
287+
if (node[ATTR_KEY]!=null) applyRef(node[ATTR_KEY].ref, null);
287288

288289
if (unmountOnly===false || node[ATTR_KEY]==null) {
289290
removeNode(node);

src/vnode.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
* @property {string | number | undefined} key The key used to identify this VNode in a list
77
* @property {object} attributes The properties of this VNode
88
*/
9-
export function VNode() {}
9+
export const VNode = function VNode() {};

test/browser/components.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,9 @@ describe('Components', () => {
113113

114114
it('should clone components', () => {
115115
function Comp () {}
116-
let instance = <Comp/>;
116+
let instance = <Comp a />;
117117
let clone = cloneElement(instance);
118-
expect(clone.prototype).to.equal(instance.prototype);
118+
expect(clone).to.deep.equal(instance);
119119
});
120120

121121
it('should render string', () => {

0 commit comments

Comments
 (0)