From 4420492dfb101693effcf6cf6e4b4a3430153c19 Mon Sep 17 00:00:00 2001 From: Jose David Rodriguez Velasco Date: Wed, 3 Nov 2021 07:03:44 -0700 Subject: [PATCH] fix(engine-dom): hydration errors due type mismatch (#2556) * fix(engine-dom): hydration errors due type mismatch * wip: normalize text in vnode creation --- .../@lwc/engine-core/src/framework/api.ts | 4 ++-- .../@lwc/engine-core/src/framework/hooks.ts | 4 ++-- .../type-coercion-to-string/index.spec.js | 21 +++++++++++++++++++ .../type-coercion-to-string/x/main/main.html | 4 ++++ .../type-coercion-to-string/x/main/main.js | 5 +++++ 5 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 packages/integration-karma/test-hydration/mismatches/type-coercion-to-string/index.spec.js create mode 100644 packages/integration-karma/test-hydration/mismatches/type-coercion-to-string/x/main/main.html create mode 100644 packages/integration-karma/test-hydration/mismatches/type-coercion-to-string/x/main/main.js diff --git a/packages/@lwc/engine-core/src/framework/api.ts b/packages/@lwc/engine-core/src/framework/api.ts index c0f13a0eee..8073283d4d 100644 --- a/packages/@lwc/engine-core/src/framework/api.ts +++ b/packages/@lwc/engine-core/src/framework/api.ts @@ -671,8 +671,8 @@ function co(text: string): VComment { } // [d]ynamic text -function d(value: any): string | any { - return value == null ? '' : value; +function d(value: any): string { + return value == null ? '' : String(value); } // [b]ind function diff --git a/packages/@lwc/engine-core/src/framework/hooks.ts b/packages/@lwc/engine-core/src/framework/hooks.ts index e1951fedea..b431d4d604 100644 --- a/packages/@lwc/engine-core/src/framework/hooks.ts +++ b/packages/@lwc/engine-core/src/framework/hooks.ts @@ -279,7 +279,7 @@ function vnodesAndElementHaveCompatibleAttrs(vnode: VNode, elm: Element): boolea // Note: intentionally ONLY matching vnodes.attrs to elm.attrs, in case SSR is adding extra attributes. for (const [attrName, attrValue] of Object.entries(attrs)) { const elmAttrValue = renderer.getAttribute(elm, attrName); - if (attrValue !== elmAttrValue) { + if (String(attrValue) !== elmAttrValue) { logError( `Mismatch hydrating element <${elm.tagName.toLowerCase()}>: attribute "${attrName}" has different values, expected "${attrValue}" but found "${elmAttrValue}"`, vnode.owner @@ -300,7 +300,7 @@ function vnodesAndElementHaveCompatibleClass(vnode: VNode, elm: Element): boolea let nodesAreCompatible = true; let vnodeClassName; - if (!isUndefined(className) && className !== elm.className) { + if (!isUndefined(className) && String(className) !== elm.className) { // className is used when class is bound to an expr. nodesAreCompatible = false; vnodeClassName = className; diff --git a/packages/integration-karma/test-hydration/mismatches/type-coercion-to-string/index.spec.js b/packages/integration-karma/test-hydration/mismatches/type-coercion-to-string/index.spec.js new file mode 100644 index 0000000000..97da6282d7 --- /dev/null +++ b/packages/integration-karma/test-hydration/mismatches/type-coercion-to-string/index.spec.js @@ -0,0 +1,21 @@ +export default { + snapshot(target) { + const p = target.shadowRoot.querySelector('p'); + return { + p, + text: p.firstChild, + }; + }, + test(target, snapshots, consoleCalls) { + expect(consoleCalls.warn).toHaveSize(0); + expect(consoleCalls.error).toHaveSize(0); + + const p = target.shadowRoot.querySelector('p'); + expect(p).toBe(snapshots.p); + expect(p.firstChild).toBe(snapshots.text); + + expect(p.textContent).toBe('123'); + expect(p.getAttribute('class')).toBe('123'); + expect(p.getAttribute('data-attr')).toBe('123'); + }, +}; diff --git a/packages/integration-karma/test-hydration/mismatches/type-coercion-to-string/x/main/main.html b/packages/integration-karma/test-hydration/mismatches/type-coercion-to-string/x/main/main.html new file mode 100644 index 0000000000..f7a376af25 --- /dev/null +++ b/packages/integration-karma/test-hydration/mismatches/type-coercion-to-string/x/main/main.html @@ -0,0 +1,4 @@ + diff --git a/packages/integration-karma/test-hydration/mismatches/type-coercion-to-string/x/main/main.js b/packages/integration-karma/test-hydration/mismatches/type-coercion-to-string/x/main/main.js new file mode 100644 index 0000000000..202b4a5835 --- /dev/null +++ b/packages/integration-karma/test-hydration/mismatches/type-coercion-to-string/x/main/main.js @@ -0,0 +1,5 @@ +import { LightningElement } from 'lwc'; + +export default class Main extends LightningElement { + num = 123; +}