Skip to content

Commit

Permalink
Add support for Component.componentDidCatch variant with single argument
Browse files Browse the repository at this point in the history
  • Loading branch information
rpetrich committed Sep 20, 2017
1 parent f7834ec commit d54d337
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 51 deletions.
1 change: 1 addition & 0 deletions config/properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"$prevProps": "__p",
"$prevState": "__s",
"$_parentComponent": "__u",
"$_ancestorComponent": "__a",
"$_componentConstructor": "_componentConstructor",
"$__html": "__html",
"$_component": "_component",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"transpile:esm": "rollup -c config/rollup.config.esm.js",
"transpile:debug": "babel debug/ -o debug.js -s",
"transpile": "npm-run-all transpile:main transpile:esm transpile:devtools transpile:debug",
"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",
"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|_ancestorC)/\" --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",
"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",
"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",
"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",
Expand Down
1 change: 1 addition & 0 deletions src/preact.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ declare namespace preact {
shouldComponentUpdate?(nextProps:PropsType,nextState:StateType,nextContext:any):boolean;
componentWillUpdate?(nextProps:PropsType,nextState:StateType,nextContext:any):void;
componentDidUpdate?(previousProps:PropsType,previousState:StateType,previousContext:any):void;
componentDidCatch?(error:any):void;
}

interface FunctionalComponent<PropsType> {
Expand Down
4 changes: 2 additions & 2 deletions src/vdom/component-recycler.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function collectComponent(component) {


/** Create a component. Normalizes differences between PFC's and classful Components. */
export function createComponent(Ctor, props, context) {
export function createComponent(Ctor, props, context, ancestorComponent) {
let list = components[Ctor.name],
inst;

Expand All @@ -28,7 +28,7 @@ export function createComponent(Ctor, props, context) {
inst.constructor = Ctor;
inst.render = doRender;
}

inst._ancestorComponent = ancestorComponent;

if (list) {
for (let i=list.length; i--; ) {
Expand Down
81 changes: 51 additions & 30 deletions src/vdom/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ export function setComponentProps(component, props, opts, context, mountAll) {
if (component.__ref) component.__ref(component);
}

export function catchErrorInComponent(error, component) {
for (; component; component = component._ancestorComponent) {
if (component.componentDidCatch) {
try {
return component.componentDidCatch(error);
} catch (e) {
error = e;
}
}
}
throw error;
}


/** Render a Component, triggering necessary lifecycle events and taking High-Order Components into account.
Expand Down Expand Up @@ -105,40 +117,45 @@ export function renderComponent(component, opts, mountAll, isChild) {
let childComponent = rendered && rendered.nodeName,
toUnmount, base;

if (typeof childComponent==='function') {
// set up high order component link
try {
if (typeof childComponent==='function') {
// set up high order component link

let childProps = getNodeProps(rendered);
inst = initialChildComponent;
let childProps = getNodeProps(rendered);
inst = initialChildComponent;

if (inst && inst.constructor===childComponent && childProps.key==inst.__key) {
setComponentProps(inst, childProps, SYNC_RENDER, context, false);
}
else {
toUnmount = inst;
if (inst && inst.constructor===childComponent && childProps.key==inst.__key) {
setComponentProps(inst, childProps, SYNC_RENDER, context, false);
}
else {
toUnmount = inst;

component._component = inst = createComponent(childComponent, childProps, context, component);
inst.nextBase = inst.nextBase || nextBase;
inst._parentComponent = component;
setComponentProps(inst, childProps, NO_RENDER, context, false);
renderComponent(inst, SYNC_RENDER, mountAll, true);
}

component._component = inst = createComponent(childComponent, childProps, context);
inst.nextBase = inst.nextBase || nextBase;
inst._parentComponent = component;
setComponentProps(inst, childProps, NO_RENDER, context, false);
renderComponent(inst, SYNC_RENDER, mountAll, true);
base = inst.base;
}
else {
cbase = initialBase;

base = inst.base;
}
else {
cbase = initialBase;

// destroy high order component link
toUnmount = initialChildComponent;
if (toUnmount) {
cbase = component._component = null;
}
// destroy high order component link
toUnmount = initialChildComponent;
if (toUnmount) {
cbase = component._component = null;
}

if (initialBase || opts===SYNC_RENDER) {
if (cbase) cbase._component = null;
base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, true);
if (initialBase || opts===SYNC_RENDER) {
if (cbase) cbase._component = null;
base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, component);
}
}
} catch (e) {
base = initialBase || document.createTextNode("");
catchErrorInComponent(e, component);
}

if (initialBase && base!==initialBase && inst!==initialChildComponent) {
Expand Down Expand Up @@ -179,7 +196,11 @@ export function renderComponent(component, opts, mountAll, isChild) {
// flushMounts();

if (component.componentDidUpdate) {
component.componentDidUpdate(previousProps, previousState, previousContext);
try {
component.componentDidUpdate(previousProps, previousState, previousContext);
} catch (e) {
catchErrorInComponent(e, component._ancestorComponent);
}
}
if (options.afterUpdate) options.afterUpdate(component);
}
Expand All @@ -199,7 +220,7 @@ export function renderComponent(component, opts, mountAll, isChild) {
* @returns {Element} dom The created/mutated element
* @private
*/
export function buildComponentFromVNode(dom, vnode, context, mountAll) {
export function buildComponentFromVNode(dom, vnode, context, mountAll, ancestorComponent) {
let c = dom && dom._component,
originalComponent = c,
oldDom = dom,
Expand All @@ -220,7 +241,7 @@ export function buildComponentFromVNode(dom, vnode, context, mountAll) {
dom = oldDom = null;
}

c = createComponent(vnode.nodeName, props, context);
c = createComponent(vnode.nodeName, props, context, ancestorComponent);
if (dom && !c.nextBase) {
c.nextBase = dom;
// passing dom/oldDom as nextBase will recycle it if unused, so bypass recycling on L229:
Expand Down
43 changes: 25 additions & 18 deletions src/vdom/diff.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ATTR_KEY } from '../constants';
import { isSameNodeType, isNamedNode } from './index';
import { buildComponentFromVNode } from './component';
import { buildComponentFromVNode, catchErrorInComponent } from './component';
import { createNode, setAccessor } from '../dom/index';
import { unmountComponent } from './component';
import options from '../options';
Expand All @@ -23,7 +23,13 @@ export function flushMounts() {
let c;
while ((c=mounts.pop())) {
if (options.afterMount) options.afterMount(c);
if (c.componentDidMount) c.componentDidMount();
if (c.componentDidMount) {
try {
c.componentDidMount();
} catch (e) {
catchErrorInComponent(e, c._ancestorComponent);
}
}
}
}

Expand All @@ -44,19 +50,20 @@ export function diff(dom, vnode, context, mountAll, parent, componentRoot) {
hydrating = dom!=null && !(ATTR_KEY in dom);
}

let ret = idiff(dom, vnode, context, mountAll, componentRoot);

// append the element if its a new parent
if (parent && ret.parentNode!==parent) parent.appendChild(ret);

// diffLevel being reduced to 0 means we're exiting the diff
if (!--diffLevel) {
hydrating = false;
// invoke queued componentDidMount lifecycle methods
if (!componentRoot) flushMounts();
let ret;
try {
return ret = idiff(dom, vnode, context, mountAll, componentRoot);
} finally {
// append the element if its a new parent
if (ret && parent && ret.parentNode!==parent) parent.appendChild(ret);

// diffLevel being reduced to 0 means we're exiting the diff
if (!--diffLevel) {
hydrating = false;
// invoke queued componentDidMount lifecycle methods
if (!componentRoot) flushMounts();
}
}

return ret;
}


Expand Down Expand Up @@ -97,7 +104,7 @@ function idiff(dom, vnode, context, mountAll, componentRoot) {
// If the VNode represents a Component, perform a component diff:
let vnodeName = vnode.nodeName;
if (typeof vnodeName==='function') {
return buildComponentFromVNode(dom, vnode, context, mountAll);
return buildComponentFromVNode(dom, vnode, context, mountAll, componentRoot);
}


Expand Down Expand Up @@ -140,7 +147,7 @@ function idiff(dom, vnode, context, mountAll, componentRoot) {
}
// otherwise, if there are existing or new children, diff them:
else if (vchildren && vchildren.length || fc!=null) {
innerDiffNode(out, vchildren, context, mountAll, hydrating || props.dangerouslySetInnerHTML!=null);
innerDiffNode(out, vchildren, context, mountAll, hydrating || props.dangerouslySetInnerHTML!=null, componentRoot);
}


Expand All @@ -162,7 +169,7 @@ function idiff(dom, vnode, context, mountAll, componentRoot) {
* @param {Boolean} mountAll
* @param {Boolean} isHydrating If `true`, consumes externally created elements similar to hydration
*/
function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
function innerDiffNode(dom, vchildren, context, mountAll, isHydrating, componentRoot) {
let originalChildren = dom.childNodes,
children = [],
keyed = {},
Expand Down Expand Up @@ -217,7 +224,7 @@ function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
}

// morph the matched/found/created DOM child to match vchild (deep)
child = idiff(child, vchild, context, mountAll);
child = idiff(child, vchild, context, mountAll, componentRoot);

f = originalChildren[i];
if (child && child!==dom && child!==f) {
Expand Down
Loading

0 comments on commit d54d337

Please sign in to comment.