• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

preactjs / preact / 13384788306

18 Feb 2025 06:42AM UTC coverage: 98.957% (-0.7%) from 99.609%
13384788306

push

github

web-flow
Merge pull request #4684 from preactjs/10.26.1

10.26.1

1090 of 1153 branches covered (94.54%)

759 of 767 relevant lines covered (98.96%)

707.14 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.06
/src/diff/index.js
1
import {
2
        EMPTY_OBJ,
3
        MATH_NAMESPACE,
4
        MODE_HYDRATE,
5
        MODE_SUSPENDED,
6
        NULL,
7
        RESET_MODE,
8
        SVG_NAMESPACE,
9
        UNDEFINED,
10
        XHTML_NAMESPACE
11
} from '../constants';
12
import { BaseComponent, getDomSibling } from '../component';
13
import { Fragment } from '../create-element';
14
import { diffChildren } from './children';
15
import { setProperty } from './props';
16
import { assign, isArray, removeNode, slice } from '../util';
17
import options from '../options';
18

19
/**
20
 * @typedef {import('../internal').ComponentChildren} ComponentChildren
21
 * @typedef {import('../internal').Component} Component
22
 * @typedef {import('../internal').PreactElement} PreactElement
23
 * @typedef {import('../internal').VNode} VNode
24
 */
25

26
/**
27
 * @template {any} T
28
 * @typedef {import('../internal').Ref<T>} Ref<T>
29
 */
30

31
/**
32
 * Diff two virtual nodes and apply proper changes to the DOM
33
 * @param {PreactElement} parentDom The parent of the DOM element
34
 * @param {VNode} newVNode The new virtual node
35
 * @param {VNode} oldVNode The old virtual node
36
 * @param {object} globalContext The current context object. Modified by
37
 * getChildContext
38
 * @param {string} namespace Current namespace of the DOM node (HTML, SVG, or MathML)
39
 * @param {Array<PreactElement>} excessDomChildren
40
 * @param {Array<Component>} commitQueue List of components which have callbacks
41
 * to invoke in commitRoot
42
 * @param {PreactElement} oldDom The current attached DOM element any new dom
43
 * elements should be placed around. Likely `null` on first render (except when
44
 * hydrating). Can be a sibling DOM element when diffing Fragments that have
45
 * siblings. In most cases, it starts out as `oldChildren[0]._dom`.
46
 * @param {boolean} isHydrating Whether or not we are in hydration
47
 * @param {any[]} refQueue an array of elements needed to invoke refs
48
 */
49
export function diff(
50
        parentDom,
51
        newVNode,
52
        oldVNode,
53
        globalContext,
54
        namespace,
55
        excessDomChildren,
56
        commitQueue,
57
        oldDom,
58
        isHydrating,
59
        refQueue
60
) {
61
        /** @type {any} */
62
        let tmp,
63
                newType = newVNode.type;
11,099✔
64

65
        // When passing through createElement it assigns the object
66
        // constructor as undefined. This to prevent JSON-injection.
67
        if (newVNode.constructor !== UNDEFINED) return NULL;
68

69
        // If the previous diff bailed out, resume creating/hydrating.
70
        if (oldVNode._flags & MODE_SUSPENDED) {
71
                isHydrating = !!(oldVNode._flags & MODE_HYDRATE);
15✔
72
                oldDom = newVNode._dom = oldVNode._dom;
16,740✔
73
                excessDomChildren = [oldDom];
74
        }
75

76
        if ((tmp = options._diff)) tmp(newVNode);
77

78
        outer: if (typeof newType == 'function') {
11,089✔
79
                try {
5,401✔
80
                        let c, isNew, oldProps, oldState, snapshot, clearProcessingException;
81
                        let newProps = newVNode.props;
82
                        const isClassComponent =
83
                                'prototype' in newType && newType.prototype.render;
10,802✔
84

85
                        // Necessary for createContext api. Setting this property will pass
86
                        // the context value as `this.context` just for this component.
87
                        tmp = newType.contextType;
88
                        let provider = tmp && globalContext[tmp._id];
5,445✔
89
                        let componentContext = tmp
5,401✔
90
                                ? provider
44✔
91
                                        ? provider.props.value
92
                                        : tmp._defaultValue
93
                                : globalContext;
94

95
                        // Get component and set it to `c`
96
                        if (oldVNode._component) {
5,401✔
97
                                c = newVNode._component = oldVNode._component;
98
                                clearProcessingException = c._processingException = c._pendingError;
99
                        } else {
100
                                // Instantiate the new component
101
                                if (isClassComponent) {
3,289✔
102
                                        // @ts-expect-error The check above verifies that newType is suppose to be constructed
103
                                        newVNode._component = c = new newType(newProps, componentContext); // eslint-disable-line new-cap
104
                                } else {
105
                                        // @ts-expect-error Trust me, Component implements the interface we want
106
                                        newVNode._component = c = new BaseComponent(
107
                                                newProps,
108
                                                componentContext
109
                                        );
110
                                        c.constructor = newType;
111
                                        c.render = doRender;
112
                                }
113
                                if (provider) provider.sub(c);
3,317✔
114

115
                                c.props = newProps;
116
                                if (!c.state) c.state = {};
6,392✔
117
                                c.context = componentContext;
118
                                c._globalContext = globalContext;
119
                                isNew = c._dirty = true;
120
                                c._renderCallbacks = [];
121
                                c._stateCallbacks = [];
122
                        }
123

124
                        // Invoke getDerivedStateFromProps
125
                        if (isClassComponent && c._nextState == NULL) {
6,122✔
126
                                c._nextState = c.state;
127
                        }
128

129
                        if (isClassComponent && newType.getDerivedStateFromProps != NULL) {
5,438✔
130
                                if (c._nextState == c.state) {
71✔
131
                                        c._nextState = assign({}, c._nextState);
132
                                }
133

134
                                assign(
135
                                        c._nextState,
136
                                        newType.getDerivedStateFromProps(newProps, c._nextState)
137
                                );
138
                        }
139

140
                        oldProps = c.props;
141
                        oldState = c.state;
142
                        c._vnode = newVNode;
143

144
                        // Invoke pre-render lifecycle methods
145
                        if (isNew) {
5,401✔
146
                                if (
147
                                        isClassComponent &&
3,285✔
148
                                        newType.getDerivedStateFromProps == NULL &&
149
                                        c.componentWillMount != NULL
150
                                ) {
151
                                        c.componentWillMount();
152
                                }
153

154
                                if (isClassComponent && c.componentDidMount != NULL) {
3,385✔
155
                                        c._renderCallbacks.push(c.componentDidMount);
156
                                }
157
                        } else {
158
                                if (
159
                                        isClassComponent &&
2,873✔
160
                                        newType.getDerivedStateFromProps == NULL &&
161
                                        newProps !== oldProps &&
162
                                        c.componentWillReceiveProps != NULL
163
                                ) {
164
                                        c.componentWillReceiveProps(newProps, componentContext);
4,181✔
165
                                }
166

167
                                if (
2,112✔
168
                                        !c._force &&
169
                                        ((c.shouldComponentUpdate != NULL &&
170
                                                c.shouldComponentUpdate(
171
                                                        newProps,
172
                                                        c._nextState,
173
                                                        componentContext
174
                                                ) === false) ||
175
                                                newVNode._original == oldVNode._original)
176
                                ) {
177
                                        // More info about this here: https://gist.github.com/JoviDeCroock/bec5f2ce93544d2e6070ef8e0036e4e8
178
                                        if (newVNode._original != oldVNode._original) {
83✔
179
                                                // When we are dealing with a bail because of sCU we have to update
180
                                                // the props, state and dirty-state.
181
                                                // when we are dealing with strict-equality we don't as the child could still
182
                                                // be dirtied see #3883
183
                                                c.props = newProps;
184
                                                c.state = c._nextState;
185
                                                c._dirty = false;
186
                                        }
187

188
                                        newVNode._dom = oldVNode._dom;
189
                                        newVNode._children = oldVNode._children;
190
                                        newVNode._children.some(vnode => {
191
                                                if (vnode) vnode._parent = newVNode;
53✔
192
                                        });
193

194
                                        for (let i = 0; i < c._stateCallbacks.length; i++) {
48✔
195
                                                c._renderCallbacks.push(c._stateCallbacks[i]);
×
196
                                        }
197
                                        c._stateCallbacks = [];
48✔
198

199
                                        if (c._renderCallbacks.length) {
48!
200
                                                commitQueue.push(c);
201
                                        }
202

203
                                        break outer;
48✔
204
                                }
205

206
                                if (c.componentWillUpdate != NULL) {
207
                                        c.componentWillUpdate(newProps, c._nextState, componentContext);
234✔
208
                                }
209

210
                                if (isClassComponent && c.componentDidUpdate != NULL) {
2,117✔
211
                                        c._renderCallbacks.push(() => {
212
                                                c.componentDidUpdate(oldProps, oldState, snapshot);
59✔
213
                                        });
214
                                }
215
                        }
216

217
                        c.context = componentContext;
218
                        c.props = newProps;
219
                        c._parentDom = parentDom;
220
                        c._force = false;
221

222
                        let renderHook = options._render,
223
                                count = 0;
224
                        if (isClassComponent) {
5,338✔
225
                                c.state = c._nextState;
226
                                c._dirty = false;
227

228
                                if (renderHook) renderHook(newVNode);
2,151✔
229

230
                                tmp = c.render(c.props, c.state, c.context);
231

232
                                for (let i = 0; i < c._stateCallbacks.length; i++) {
1,455✔
233
                                        c._renderCallbacks.push(c._stateCallbacks[i]);
13✔
234
                                }
235
                                c._stateCallbacks = [];
1,403✔
236
                        } else {
237
                                do {
3,883✔
238
                                        c._dirty = false;
3,927✔
239
                                        if (renderHook) renderHook(newVNode);
6,488✔
240

241
                                        tmp = c.render(c.props, c.state, c.context);
242

243
                                        // Handle setState called in render, see #2553
244
                                        c.state = c._nextState;
245
                                } while (c._dirty && ++count < 25);
3,847✔
246
                        }
247

248
                        // Handle setState called in render, see #2553
249
                        c.state = c._nextState;
250

251
                        if (c.getChildContext != NULL) {
252
                                globalContext = assign(assign({}, globalContext), c.getChildContext());
151✔
253
                        }
254

255
                        if (isClassComponent && !isNew && c.getSnapshotBeforeUpdate != NULL) {
6,580✔
256
                                snapshot = c.getSnapshotBeforeUpdate(oldProps, oldState);
257
                        }
258

259
                        let isTopLevelFragment =
260
                                tmp != NULL && tmp.type === Fragment && tmp.key == NULL;
4,836✔
261
                        let renderResult = isTopLevelFragment ? tmp.props.children : tmp;
5,160✔
262

263
                        if (isTopLevelFragment) {
5,160✔
264
                                tmp.props.children = NULL;
265
                        }
266

267
                        oldDom = diffChildren(
268
                                parentDom,
269
                                isArray(renderResult) ? renderResult : [renderResult],
5,160✔
270
                                newVNode,
271
                                oldVNode,
272
                                globalContext,
273
                                namespace,
274
                                excessDomChildren,
275
                                commitQueue,
276
                                oldDom,
277
                                isHydrating,
278
                                refQueue
279
                        );
280

281
                        c.base = newVNode._dom;
282

283
                        // We successfully rendered this VNode, unset any stored hydration/bailout state:
284
                        newVNode._flags &= RESET_MODE;
285

286
                        if (c._renderCallbacks.length) {
5,430✔
287
                                commitQueue.push(c);
288
                        }
289

290
                        if (clearProcessingException) {
5,131✔
291
                                c._pendingError = c._processingException = NULL;
292
                        }
293
                } catch (e) {
294
                        newVNode._original = NULL;
295
                        // if hydrating or creating initial tree, bailout preserves DOM:
296
                        if (isHydrating || excessDomChildren != NULL) {
222✔
297
                                if (e.then) {
18✔
298
                                        newVNode._flags |= isHydrating
299
                                                ? MODE_HYDRATE | MODE_SUSPENDED
300
                                                : MODE_SUSPENDED;
301

302
                                        while (oldDom && oldDom.nodeType == 8 && oldDom.nextSibling) {
17✔
303
                                                oldDom = oldDom.nextSibling;
1✔
304
                                        }
305

306
                                        excessDomChildren[excessDomChildren.indexOf(oldDom)] = NULL;
17✔
307
                                        newVNode._dom = oldDom;
308
                                } else {
309
                                        for (let i = excessDomChildren.length; i--; ) {
1✔
310
                                                removeNode(excessDomChildren[i]);
1✔
311
                                        }
312
                                }
313
                        } else {
314
                                newVNode._dom = oldVNode._dom;
204✔
315
                                newVNode._children = oldVNode._children;
316
                        }
317
                        options._catchError(e, newVNode, oldVNode);
222✔
318
                }
319
        } else if (
320
                excessDomChildren == NULL &&
321
                newVNode._original == oldVNode._original
5,443✔
322
        ) {
323
                newVNode._children = oldVNode._children;
5,688✔
324
                newVNode._dom = oldVNode._dom;
325
        } else {
326
                oldDom = newVNode._dom = diffElementNodes(
327
                        oldVNode._dom,
328
                        newVNode,
329
                        oldVNode,
330
                        globalContext,
331
                        namespace,
332
                        excessDomChildren,
333
                        commitQueue,
334
                        isHydrating,
335
                        refQueue
336
                );
337
        }
338

339
        if ((tmp = options.diffed)) tmp(newVNode);
340

341
        return newVNode._flags & MODE_SUSPENDED ? undefined : oldDom;
11,042✔
342
}
343

344
/**
345
 * @param {Array<Component>} commitQueue List of components
346
 * which have callbacks to invoke in commitRoot
347
 * @param {VNode} root
348
 */
349
export function commitRoot(commitQueue, root, refQueue) {
350
        for (let i = 0; i < refQueue.length; i++) {
1,888✔
351
                applyRef(refQueue[i], refQueue[++i], refQueue[++i]);
91✔
352
        }
353

354
        if (options._commit) options._commit(root, commitQueue);
1,888✔
355

356
        commitQueue.some(c => {
357
                try {
299✔
358
                        // @ts-expect-error Reuse the commitQueue variable here so the type changes
359
                        commitQueue = c._renderCallbacks;
299✔
360
                        c._renderCallbacks = [];
361
                        commitQueue.some(cb => {
362
                                // @ts-expect-error See above comment on commitQueue
363
                                cb.call(c);
179✔
364
                        });
365
                } catch (e) {
366
                        options._catchError(e, c._vnode);
4✔
367
                }
368
        });
369
}
370

371
/**
372
 * Diff two virtual nodes representing DOM element
373
 * @param {PreactElement} dom The DOM element representing the virtual nodes
374
 * being diffed
375
 * @param {VNode} newVNode The new virtual node
376
 * @param {VNode} oldVNode The old virtual node
377
 * @param {object} globalContext The current context object
378
 * @param {string} namespace Current namespace of the DOM node (HTML, SVG, or MathML)
379
 * @param {Array<PreactElement>} excessDomChildren
380
 * @param {Array<Component>} commitQueue List of components which have callbacks
381
 * to invoke in commitRoot
382
 * @param {boolean} isHydrating Whether or not we are in hydration
383
 * @param {any[]} refQueue an array of elements needed to invoke refs
384
 * @returns {PreactElement}
385
 */
386
function diffElementNodes(
387
        dom,
388
        newVNode,
389
        oldVNode,
390
        globalContext,
391
        namespace,
392
        excessDomChildren,
393
        commitQueue,
394
        isHydrating,
395
        refQueue
396
) {
397
        let oldProps = oldVNode.props;
5,679✔
398
        let newProps = newVNode.props;
5,679✔
399
        let nodeType = /** @type {string} */ (newVNode.type);
5,679✔
400
        /** @type {any} */
401
        let i;
402
        /** @type {{ __html?: string }} */
403
        let newHtml;
404
        /** @type {{ __html?: string }} */
405
        let oldHtml;
406
        /** @type {ComponentChildren} */
407
        let newChildren;
408
        let value;
409
        let inputValue;
410
        let checked;
411

412
        // Tracks entering and exiting namespaces when descending through the tree.
413
        if (nodeType == 'svg') namespace = SVG_NAMESPACE;
414
        else if (nodeType == 'math') namespace = MATH_NAMESPACE;
415
        else if (!namespace) namespace = XHTML_NAMESPACE;
5,676✔
416

417
        if (excessDomChildren != NULL) {
5,679✔
418
                for (i = 0; i < excessDomChildren.length; i++) {
245✔
419
                        value = excessDomChildren[i];
420

421
                        // if newVNode matches an element in excessDomChildren or the `dom`
422
                        // argument matches an element in excessDomChildren, remove it from
423
                        // excessDomChildren so it isn't later removed in diffChildren
424
                        if (
380✔
425
                                value &&
426
                                'setAttribute' in value == !!nodeType &&
427
                                (nodeType ? value.localName == nodeType : value.nodeType == 3)
212✔
428
                        ) {
429
                                dom = value;
430
                                excessDomChildren[i] = NULL;
431
                                break;
207✔
432
                        }
433
                }
434
        }
435

436
        if (dom == NULL) {
5,679✔
437
                if (nodeType == NULL) {
3,629✔
438
                        return document.createTextNode(newProps);
1,722✔
439
                }
440

441
                dom = document.createElementNS(
442
                        namespace,
443
                        nodeType,
444
                        newProps.is && newProps
1,908✔
445
                );
446

447
                // we are creating a new node, so we can assume this is a new subtree (in
448
                // case we are hydrating), this deopts the hydrate
449
                if (isHydrating) {
1,912✔
450
                        if (options._hydrationMismatch)
6✔
451
                                options._hydrationMismatch(newVNode, excessDomChildren);
452
                        isHydrating = false;
453
                }
454
                // we created a new parent, so none of the previously attached children can be reused:
455
                excessDomChildren = NULL;
456
        }
457

458
        if (nodeType === NULL) {
3,957✔
459
                // During hydration, we still have to split merged text from SSR'd HTML.
460
                if (oldProps !== newProps && (!isHydrating || dom.data !== newProps)) {
926✔
461
                        dom.data = newProps;
462
                }
463
        } else {
464
                // If excessDomChildren was not null, repopulate it with the current element's children:
465
                excessDomChildren = excessDomChildren && slice.call(dom.childNodes);
3,147✔
466

467
                oldProps = oldVNode.props || EMPTY_OBJ;
8,085✔
468

469
                // If we are in a situation where we are not hydrating but are using
470
                // existing DOM (e.g. replaceNode) we should read the existing DOM
471
                // attributes to diff them
472
                if (!isHydrating && excessDomChildren != NULL) {
3,031!
473
                        oldProps = {};
474
                        for (i = 0; i < dom.attributes.length; i++) {
×
475
                                value = dom.attributes[i];
476
                                oldProps[value.name] = value.value;
×
477
                        }
478
                }
479

480
                for (i in oldProps) {
3,031✔
481
                        value = oldProps[i];
482
                        if (i == 'children') {
1,075✔
483
                        } else if (i == 'dangerouslySetInnerHTML') {
166!
484
                                oldHtml = value;
166✔
485
                        } else if (!(i in newProps)) {
486
                                if (
11!
487
                                        (i == 'value' && 'defaultValue' in newProps) ||
22!
488
                                        (i == 'checked' && 'defaultChecked' in newProps)
489
                                ) {
490
                                        continue;
×
491
                                }
492
                                setProperty(dom, i, NULL, value, namespace);
11✔
493
                        }
494
                }
495

496
                // During hydration, props are not diffed at all (including dangerouslySetInnerHTML)
497
                // @TODO we should warn in debug mode when props don't match here.
498
                for (i in newProps) {
3,031✔
499
                        value = newProps[i];
3,257✔
500
                        if (i == 'children') {
3,257✔
501
                                newChildren = value;
502
                        } else if (i == 'dangerouslySetInnerHTML') {
522✔
503
                                newHtml = value;
504
                        } else if (i == 'value') {
521✔
505
                                inputValue = value;
506
                        } else if (i == 'checked') {
491✔
507
                                checked = value;
508
                        } else if (
509
                                (!isHydrating || typeof value == 'function') &&
1,363✔
510
                                oldProps[i] !== value
511
                        ) {
512
                                setProperty(dom, i, value, oldProps[i], namespace);
513
                        }
514
                }
515

516
                // If the new vnode didn't have dangerouslySetInnerHTML, diff its children
517
                if (newHtml) {
3,031✔
518
                        // Avoid re-applying the same '__html' if it did not changed between re-render
519
                        if (
520
                                !isHydrating &&
1!
521
                                (!oldHtml ||
522
                                        (newHtml.__html !== oldHtml.__html &&
523
                                                newHtml.__html !== dom.innerHTML))
524
                        ) {
525
                                dom.innerHTML = newHtml.__html;
526
                        }
527

528
                        newVNode._children = [];
529
                } else {
530
                        if (oldHtml) dom.innerHTML = '';
3,030!
531

532
                        diffChildren(
533
                                // @ts-expect-error
534
                                newVNode.type === 'template' ? dom.content : dom,
3,030!
535
                                isArray(newChildren) ? newChildren : [newChildren],
3,030✔
536
                                newVNode,
537
                                oldVNode,
538
                                globalContext,
539
                                nodeType == 'foreignObject' ? XHTML_NAMESPACE : namespace,
540
                                excessDomChildren,
541
                                commitQueue,
542
                                excessDomChildren
3,030✔
543
                                        ? excessDomChildren[0]
544
                                        : oldVNode._children && getDomSibling(oldVNode, 0),
3,923✔
545
                                isHydrating,
546
                                refQueue
547
                        );
548

549
                        // Remove children that are not part of any vnode.
550
                        if (excessDomChildren != NULL) {
3,030✔
551
                                for (i = excessDomChildren.length; i--; ) {
115✔
552
                                        removeNode(excessDomChildren[i]);
159✔
553
                                }
554
                        }
555
                }
556

557
                // As above, don't diff props during hydration
558
                if (!isHydrating) {
3,031✔
559
                        i = 'value';
560
                        if (nodeType == 'progress' && inputValue == NULL) {
5,830!
561
                                dom.removeAttribute('value');
2,974!
562
                        } else if (
563
                                inputValue !== UNDEFINED &&
564
                                // #2756 For the <progress>-element the initial value is 0,
565
                                // despite the attribute not being present. When the attribute
566
                                // is missing the progress bar is treated as indeterminate.
567
                                // To fix that we'll always update it when it is 0 for progress elements
568
                                (inputValue !== dom[i] ||
569
                                        (nodeType == 'progress' && !inputValue) ||
570
                                        // This is only for IE 11 to fix <select> value not being updated.
571
                                        // To avoid a stale select value we need to set the option.value
572
                                        // again, which triggers IE11 to re-evaluate the select value
573
                                        (nodeType == 'option' && inputValue !== oldProps[i]))
574
                        ) {
575
                                setProperty(dom, i, inputValue, oldProps[i], namespace);
576
                        }
577

578
                        i = 'checked';
2,920!
579
                        if (checked !== UNDEFINED && checked !== dom[i]) {
580
                                setProperty(dom, i, checked, oldProps[i], namespace);
581
                        }
582
                }
583
        }
584

585
        return dom;
3,957✔
586
}
587

588
/**
589
 * Invoke or update a ref, depending on whether it is a function or object ref.
590
 * @param {Ref<any> & { _unmount?: unknown }} ref
591
 * @param {any} value
592
 * @param {VNode} vnode
593
 */
594
export function applyRef(ref, value, vnode) {
595
        try {
129✔
596
                if (typeof ref == 'function') {
129✔
597
                        let hasRefUnmount = typeof ref._unmount == 'function';
82✔
598
                        if (hasRefUnmount) {
82!
599
                                // @ts-ignore TS doesn't like moving narrowing checks into variables
600
                                ref._unmount();
601
                        }
602

603
                        if (!hasRefUnmount || value != NULL) {
164✔
604
                                // Store the cleanup function on the function
605
                                // instance object itself to avoid shape
606
                                // transitioning vnode
607
                                ref._unmount = ref(value);
608
                        }
609
                } else ref.current = value;
47✔
610
        } catch (e) {
611
                options._catchError(e, vnode);
8✔
612
        }
613
}
614

615
/**
616
 * Unmount a virtual node from the tree and apply DOM changes
617
 * @param {VNode} vnode The virtual node to unmount
618
 * @param {VNode} parentVNode The parent of the VNode that initiated the unmount
619
 * @param {boolean} [skipRemove] Flag that indicates that a parent node of the
620
 * current element is already detached from the DOM.
621
 */
622
export function unmount(vnode, parentVNode, skipRemove) {
623
        let r;
624
        if (options.unmount) options.unmount(vnode);
4,417✔
625

626
        if ((r = vnode.ref)) {
627
                if (!r.current || r.current === vnode._dom) {
628
                        applyRef(r, NULL, parentVNode);
629
                }
630
        }
631

632
        if ((r = vnode._component) != NULL) {
1,748✔
633
                if (r.componentWillUnmount) {
499✔
634
                        try {
60✔
635
                                r.componentWillUnmount();
60✔
636
                        } catch (e) {
637
                                options._catchError(e, parentVNode);
2✔
638
                        }
639
                }
640

641
                r.base = r._parentDom = NULL;
642
        }
643

644
        if ((r = vnode._children)) {
1,748✔
645
                for (let i = 0; i < r.length; i++) {
1,077✔
646
                        if (r[i]) {
1,139✔
647
                                unmount(
648
                                        r[i],
649
                                        parentVNode,
650
                                        skipRemove || typeof vnode.type != 'function'
2,083✔
651
                                );
652
                        }
653
                }
654
        }
655

656
        if (!skipRemove) {
2,929✔
657
                removeNode(vnode._dom);
658
        }
659

660
        vnode._component = vnode._parent = vnode._dom = UNDEFINED;
661
}
662

663
/** The `.render()` method for a PFC backing instance. */
664
function doRender(props, state, context) {
665
        return this.constructor(props, context);
3,925✔
666
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc