})
test('force hydrate prop with `.prop` modifier', () => {
- const { container } = mountWithHydration(
- '<input type="checkbox" :indeterminate.prop="true">',
- () =>
- h('input', {
- type: 'checkbox',
- '.indeterminate': true,
- }),
+ const { container } = mountWithHydration('<input type="checkbox">', () =>
+ h('input', {
+ type: 'checkbox',
+ '.indeterminate': true,
+ }),
)
expect((container.firstChild! as any).indeterminate).toBe(true)
})
mountWithHydration(`<select multiple></div>`, () =>
h('select', { multiple: 'multiple' }),
)
+ expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
+
+ mountWithHydration(`<div></div>`, () => h('div', { id: 'foo' }))
+ expect(`Hydration attribute mismatch`).toHaveBeenWarnedTimes(1)
+
+ mountWithHydration(`<div id="bar"></div>`, () => h('div', { id: 'foo' }))
+ expect(`Hydration attribute mismatch`).toHaveBeenWarnedTimes(2)
+ })
+
+ test('attr special case: textarea value', () => {
mountWithHydration(`<textarea>foo</textarea>`, () =>
h('textarea', { value: 'foo' }),
)
)
expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
- mountWithHydration(`<div></div>`, () => h('div', { id: 'foo' }))
+ mountWithHydration(`<textarea>foo</textarea>`, () =>
+ h('textarea', { value: 'bar' }),
+ )
expect(`Hydration attribute mismatch`).toHaveBeenWarned()
-
- mountWithHydration(`<div id="bar"></div>`, () => h('div', { id: 'foo' }))
- expect(`Hydration attribute mismatch`).toHaveBeenWarnedTimes(2)
})
test('boolean attr handling', () => {
)
expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
})
+
+ test('should not warn against object values', () => {
+ mountWithHydration(`<input />`, () => h('input', { from: {} }))
+ expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
+ })
})
})
isBooleanAttr,
isKnownHtmlAttr,
isKnownSvgAttr,
+ isObject,
isOn,
isReservedProp,
isString,
expected = includeBooleanAttr(clientValue)
} else {
// #10000 some attrs such as textarea.value can't be get by `hasAttribute`
- actual = el.hasAttribute(key)
- ? el.getAttribute(key)
- : key in el
- ? el[key as keyof typeof el]
- : ''
- expected = clientValue == null ? '' : String(clientValue)
+ if (el.hasAttribute(key)) {
+ actual = el.getAttribute(key)
+ } else if (key in el) {
+ const serverValue = el[key as keyof typeof el]
+ if (!isObject(serverValue)) {
+ actual = serverValue == null ? '' : String(serverValue)
+ }
+ }
+ if (!isObject(clientValue)) {
+ expected = clientValue == null ? '' : String(clientValue)
+ }
}
if (actual !== expected) {
mismatchType = `attribute`
if (mismatchType) {
const format = (v: any) =>
v === false ? `(not rendered)` : `${mismatchKey}="${v}"`
- warn(
- `Hydration ${mismatchType} mismatch on`,
- el,
+ const preSegment = `Hydration ${mismatchType} mismatch on`
+ const postSegment =
`\n - rendered on server: ${format(actual)}` +
- `\n - expected on client: ${format(expected)}` +
- `\n Note: this mismatch is check-only. The DOM will not be rectified ` +
- `in production due to performance overhead.` +
- `\n You should fix the source of the mismatch.`,
- )
+ `\n - expected on client: ${format(expected)}` +
+ `\n Note: this mismatch is check-only. The DOM will not be rectified ` +
+ `in production due to performance overhead.` +
+ `\n You should fix the source of the mismatch.`
+ if (__TEST__) {
+ // during tests, log the full message in one single string for easier
+ // debugging.
+ warn(`${preSegment} ${el.tagName}${postSegment}`)
+ } else {
+ warn(preSegment, el, postSegment)
+ }
return true
}
return false