Skip to content

Commit 35e77a1

Browse files
authored
allow overwriting hook properties (#3867)
This is required for backwards compatibility. When re-writing the code to support giving classes, I added a check to prevent overwriting the hook's own properties, but this is actually used by hooks in the wild, so we only log a warning instead.
1 parent af2189b commit 35e77a1

File tree

2 files changed

+46
-21
lines changed

2 files changed

+46
-21
lines changed

assets/js/phoenix_live_view/view_hook.ts

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -288,28 +288,12 @@ export class ViewHook implements HookInterface {
288288

289289
for (const key in callbacks) {
290290
if (Object.prototype.hasOwnProperty.call(callbacks, key)) {
291+
(this as any)[key] = callbacks[key];
292+
// for backwards compatibility, we allow the overwrite, but we log a warning
291293
if (protectedProps.has(key)) {
292-
// Optionally log a warning if a user tries to overwrite a protected property/method
293-
// For now, we silently prioritize the ViewHook's own properties/methods.
294-
if (
295-
typeof (this as any)[key] === "function" &&
296-
typeof callbacks[key] !== "function" &&
297-
![
298-
"mounted",
299-
"beforeUpdate",
300-
"updated",
301-
"destroyed",
302-
"disconnected",
303-
"reconnected",
304-
].includes(key)
305-
) {
306-
// If core method is a function and callback is not, likely an error from user.
307-
console.warn(
308-
`Hook object for element #${el.id} attempted to overwrite core method '${key}' with a non-function value. This is not allowed.`,
309-
);
310-
}
311-
} else {
312-
(this as any)[key] = callbacks[key];
294+
console.warn(
295+
`Hook object for element #${el.id} overwrites core property '${key}'!`,
296+
);
313297
}
314298
}
315299
}

assets/test/view_test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,47 @@ describe("View Hooks", function () {
14771477
expect(toHTML).toBe("updated");
14781478
expect(view.el.firstChild.innerHTML).toBe("updated");
14791479
});
1480+
1481+
test("can overwrite property", async () => {
1482+
let customHandleEventCalled = false;
1483+
const Hooks = {
1484+
Upcase: {
1485+
mounted() {
1486+
this.handleEvent = () => {
1487+
customHandleEventCalled = true;
1488+
};
1489+
this.el.innerHTML = this.el.innerHTML.toUpperCase();
1490+
},
1491+
updated() {
1492+
this.handleEvent();
1493+
},
1494+
},
1495+
};
1496+
liveSocket = new LiveSocket("/live", Socket, { hooks: Hooks });
1497+
const el = liveViewDOM();
1498+
1499+
const view = simulateJoinedView(el, liveSocket);
1500+
1501+
view.onJoin({
1502+
rendered: {
1503+
s: ['<h2 id="up" phx-hook="Upcase">test mount</h2>'],
1504+
fingerprint: 123,
1505+
},
1506+
liveview_version,
1507+
});
1508+
expect(view.el.firstChild.innerHTML).toBe("TEST MOUNT");
1509+
expect(Object.keys(view.viewHooks)).toHaveLength(1);
1510+
1511+
expect(customHandleEventCalled).toBe(false);
1512+
view.update(
1513+
{
1514+
s: ['<h2 id="up" phx-hook="Upcase">test update</h2>'],
1515+
fingerprint: 123,
1516+
},
1517+
[],
1518+
);
1519+
expect(customHandleEventCalled).toBe(true);
1520+
});
14801521
});
14811522

14821523
function liveViewComponent() {

0 commit comments

Comments
 (0)