Skip to content

Commit 58b1fa5

Browse files
authored
fix(hydration): ensure hydrated event listeners have bound instance (#4529)
fix #4479
1 parent d8a36d0 commit 58b1fa5

File tree

2 files changed

+72
-2
lines changed

2 files changed

+72
-2
lines changed

packages/runtime-core/__tests__/hydration.spec.ts

+54
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,60 @@ describe('SSR hydration', () => {
461461
expect(text.textContent).toBe('bye')
462462
})
463463

464+
test('handle click error in ssr mode', async () => {
465+
const App = {
466+
setup() {
467+
const throwError = () => {
468+
throw new Error('Sentry Error')
469+
}
470+
return { throwError }
471+
},
472+
template: `
473+
<div>
474+
<button class="parent-click" @click="throwError">click me</button>
475+
</div>`
476+
}
477+
478+
const container = document.createElement('div')
479+
// server render
480+
container.innerHTML = await renderToString(h(App))
481+
// hydrate
482+
const app = createSSRApp(App)
483+
const handler = (app.config.errorHandler = jest.fn())
484+
app.mount(container)
485+
// assert interactions
486+
// parent button click
487+
triggerEvent('click', container.querySelector('.parent-click')!)
488+
expect(handler).toHaveBeenCalled()
489+
})
490+
491+
test('handle blur error in ssr mode', async () => {
492+
const App = {
493+
setup() {
494+
const throwError = () => {
495+
throw new Error('Sentry Error')
496+
}
497+
return { throwError }
498+
},
499+
template: `
500+
<div>
501+
<input class="parent-click" @blur="throwError"/>
502+
</div>`
503+
}
504+
505+
const container = document.createElement('div')
506+
// server render
507+
container.innerHTML = await renderToString(h(App))
508+
// hydrate
509+
const app = createSSRApp(App)
510+
const handler = (app.config.errorHandler = jest.fn())
511+
app.mount(container)
512+
// assert interactions
513+
// parent blur event
514+
triggerEvent('blur', container.querySelector('.parent-click')!)
515+
expect(handler).toHaveBeenCalled()
516+
})
517+
464518
test('Suspense', async () => {
465519
const AsyncChild = {
466520
async setup() {

packages/runtime-core/src/hydration.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -287,13 +287,29 @@ export function createHydrationFunctions(
287287
(forcePatchValue && key.endsWith('value')) ||
288288
(isOn(key) && !isReservedProp(key))
289289
) {
290-
patchProp(el, key, null, props[key])
290+
patchProp(
291+
el,
292+
key,
293+
null,
294+
props[key],
295+
false,
296+
undefined,
297+
parentComponent
298+
)
291299
}
292300
}
293301
} else if (props.onClick) {
294302
// Fast path for click listeners (which is most often) to avoid
295303
// iterating through props.
296-
patchProp(el, 'onClick', null, props.onClick)
304+
patchProp(
305+
el,
306+
'onClick',
307+
null,
308+
props.onClick,
309+
false,
310+
undefined,
311+
parentComponent
312+
)
297313
}
298314
}
299315
// vnode / directive hooks

0 commit comments

Comments
 (0)