diff --git a/compat/src/index.js b/compat/src/index.js index a35478a2a6..b5e7b5ff58 100644 --- a/compat/src/index.js +++ b/compat/src/index.js @@ -92,7 +92,7 @@ function findDOMNode(component) { return component; } - return getChildDom(component._internal); + return getChildDom(component._internal, 0); } /** @@ -120,7 +120,7 @@ const StrictMode = Fragment; * @param {Arg} [arg] Optional arugment that can be passed to the callback * @returns */ -const flushSync = (callback, arg) => callback(arg) +const flushSync = (callback, arg) => callback(arg); export * from 'preact/hooks'; export { diff --git a/hooks/src/index.js b/hooks/src/index.js index 1729a0a630..1797a7e99a 100644 --- a/hooks/src/index.js +++ b/hooks/src/index.js @@ -144,7 +144,7 @@ export function useReducer(reducer, initialState, init) { const nextValue = hookState._reducer(hookState._value[0], action); if (hookState._value[0] !== nextValue) { hookState._value = [nextValue, hookState._value[1]]; - hookState._internal.rerender(hookState._internal); + hookState._internal.render(); } } ]; diff --git a/jsx-runtime/src/index.js b/jsx-runtime/src/index.js index 9de5249d3d..c1754d70f6 100644 --- a/jsx-runtime/src/index.js +++ b/jsx-runtime/src/index.js @@ -28,8 +28,11 @@ function createVNode(type, props, key, __source, __self) { // forwardRef components in the future, but that should happen via // a separate PR. let normalizedProps = {}; + let ref; for (let i in props) { - if (i != 'ref') { + if (i === 'ref') { + ref = props[i]; + } else { normalizedProps[i] = props[i]; } } @@ -38,24 +41,15 @@ function createVNode(type, props, key, __source, __self) { type, props: normalizedProps, key, - ref: props && props.ref, + ref, constructor: undefined, _vnodeId: --vnodeId, __source, __self }; - // If a Component VNode, check for and apply defaultProps. - // Note: `type` is often a String, and can be `undefined` in development. - let defaults, i; - if (typeof type === 'function' && (defaults = type.defaultProps)) { - for (i in defaults) - if (normalizedProps[i] === undefined) { - normalizedProps[i] = defaults[i]; - } - } - if (options.vnode) options.vnode(vnode); + return vnode; } diff --git a/jsx-runtime/test/browser/jsx-runtime.test.js b/jsx-runtime/test/browser/jsx-runtime.test.js index 83dc82afec..aef900a834 100644 --- a/jsx-runtime/test/browser/jsx-runtime.test.js +++ b/jsx-runtime/test/browser/jsx-runtime.test.js @@ -31,7 +31,8 @@ describe('Babel jsx/jsxDEV', () => { expect(vnode.key).to.equal('foo'); }); - it('should apply defaultProps', () => { + // We no longer support defaultProps. + it.skip('should apply defaultProps', () => { class Foo extends Component { render() { return
; @@ -48,7 +49,7 @@ describe('Babel jsx/jsxDEV', () => { }); }); - it('should keep props over defaultProps', () => { + it.skip('should keep props over defaultProps', () => { class Foo extends Component { render() { return ; @@ -81,6 +82,7 @@ describe('Babel jsx/jsxDEV', () => { delete jsxVNode.__source; delete jsxVNode._vnodeId; delete elementVNode._vnodeId; + // expect(jsxVNode.constructor).to.equal(elementVNode.constructor); expect(jsxVNode).to.deep.equal(elementVNode); }); diff --git a/mangle.json b/mangle.json index 14b3d639e2..ebd5ae7c4b 100644 --- a/mangle.json +++ b/mangle.json @@ -34,6 +34,7 @@ "props": { "cname": 6, "props": { + "$flags": "f", "$_vnodeId": "__v", "$_cleanup": "__c", "$_afterPaintQueued": "__a", diff --git a/src/clone-element.js b/src/clone-element.js index 62669d8202..dba60c261b 100644 --- a/src/clone-element.js +++ b/src/clone-element.js @@ -1,4 +1,5 @@ -import { createVNode } from './create-element'; +import { EMPTY_ARR } from './constants'; +import { createElement } from './create-element'; /** * Clones the given VNode, optionally adding attributes/props and replacing its children. @@ -8,33 +9,13 @@ import { createVNode } from './create-element'; * @returns {import('./internal').VNode} */ export function cloneElement(vnode, props, children) { - let normalizedProps = Object.assign({}, vnode.props), - key, - ref, - i; - - for (i in props) { - if (i == 'key') key = props[i]; - else if (i == 'ref') ref = props[i]; - else normalizedProps[i] = props[i]; - } - if (arguments.length > 3) { - children = [children]; - for (i = 3; i < arguments.length; i++) { - children.push(arguments[i]); - } - } - - if (arguments.length > 2) { - normalizedProps.children = children; + children = EMPTY_ARR.slice.call(arguments, 2); } - return createVNode( + return createElement( vnode.type, - normalizedProps, - key || vnode.key, - ref || vnode.ref, - 0 + Object.assign({ key: vnode.key, ref: vnode.ref }, vnode.props, props), + children ); } diff --git a/src/component.js b/src/component.js index 1b63e02608..9dc37a059a 100644 --- a/src/component.js +++ b/src/component.js @@ -1,4 +1,4 @@ -import { addCommitCallback, commitRoot } from './diff/commit'; +import { commitRoot } from './diff/commit'; import options from './options'; import { createVNode, Fragment } from './create-element'; import { patch } from './diff/patch'; @@ -47,35 +47,39 @@ Component.prototype.setState = function(update, callback) { update = update(Object.assign({}, s), this.props); } - if (update) { - Object.assign(s, update); - } + Object.assign(s, update); // Skip update if updater function returned null if (update == null) return; - if (this._internal) { - if (callback) addCommitCallback(this._internal, callback.bind(this)); - this._internal.rerender(this._internal); - } + // The 0 flag value here prevents FORCE_UPDATE from being set + renderComponentInstance.call(this, callback, 0); }; /** * Immediately perform a synchronous re-render of the component + * @param {() => void} [callback] A function to call after re-rendering completes + * @this {import('./internal').Component} + */ +Component.prototype.forceUpdate = renderComponentInstance; + +/** + * Immediately perform a synchronous re-render of the component. + * This method is the implementation of forceUpdate() for class components. + * @param {() => void} [callback] A function to call after rendering completes + * @param {number} [flags = FORCE_UPDATE] Flags to set. Defaults to FORCE_UPDATE. * @this {import('./internal').Component} - * @param {() => void} [callback] A function to be called after component is - * re-rendered */ -Component.prototype.forceUpdate = function(callback) { +export function renderComponentInstance(callback, flags) { if (this._internal) { // Set render mode so that we can differentiate where the render request - // is coming from. We need this because forceUpdate should never call - // shouldComponentUpdate - this._internal.flags |= FORCE_UPDATE; - if (callback) addCommitCallback(this._internal, callback.bind(this)); - this._internal.rerender(this._internal); + // is coming from (eg: forceUpdate should never call shouldComponentUpdate). + this._internal.flags |= flags == null ? FORCE_UPDATE : flags; + this._internal.render(callback); + // Note: the above is equivalent to invoking enqueueRender: + // enqueueRender.call(this._internal, callback); } -}; +} /** * Accepts `props` and `state`, and returns a new Virtual DOM tree to build. @@ -90,36 +94,36 @@ Component.prototype.forceUpdate = function(callback) { Component.prototype.render = Fragment; /** - * @param {import('./internal').Component} internal The internal to rerender + * Render an Internal that has been marked + * @param {import('./internal').Internal} internal The internal to rerender */ -function rerender(internal) { - if (~internal.flags & MODE_UNMOUNTING && internal.flags & DIRTY_BIT) { - let parentDom = getParentDom(internal); - let startDom = - (internal.flags & (MODE_HYDRATE | MODE_SUSPENDED)) === - (MODE_HYDRATE | MODE_SUSPENDED) - ? internal._dom - : getDomSibling(internal, 0); - - const vnode = createVNode( - internal.type, - internal.props, - internal.key, // @TODO we shouldn't need to actually pass these - internal.ref, // since the mode flag should bypass key/ref handling - 0 - ); - - const commitQueue = []; - patch(parentDom, vnode, internal, commitQueue, startDom); - commitRoot(commitQueue, internal); - } -} +const renderQueuedInternal = internal => { + const commitQueue = []; + + const vnode = createVNode(internal.type, internal.props); + + // Don't render unmounting/unmounted trees: + if (internal.flags & MODE_UNMOUNTING) return; + + // Don't render trees already rendered in this pass: + if (!(internal.flags & DIRTY_BIT)) return; + + let parentDom = getParentDom(internal); + let startDom = + (internal.flags & (MODE_HYDRATE | MODE_SUSPENDED)) === + (MODE_HYDRATE | MODE_SUSPENDED) + ? internal._dom + : getDomSibling(internal, 0); + + patch(parentDom, vnode, internal, commitQueue, startDom); + commitRoot(commitQueue, internal); +}; /** - * The render queue - * @type {Arrayextends preact.ComponentClass
{ export type ComponentType
= ComponentClass
| FunctionComponent
;
export interface PreactElement extends HTMLElement {
+ /** The root Internal of a tree, if one exists (minified to `__k`) */
_children?: Internal {
/** Polymorphic property to store extensions like hooks on */
data: object;
/** The function that triggers in-place re-renders for an internal */
- rerender: () => void;
+ render: (callback?: () => void) => void;
/** children Internal nodes */
_children: Internal[];
@@ -172,3 +174,8 @@ export interface PreactContext extends preact.Context