handleInfo() is not run timely when called from callbacks
trondsg opened this issue · comments
I cannot get handleInfo() to run automatically when called from a callback (e.g. establishing a database connection).
I am using liveview 0.7.5. and node with express.
Putting this in mount():
setTimeout(()=>socket.sendInfo("from mount->setTimeout"),0);
Results in sendInfo() receiving the message after the next handleEvent().
export const myLiveView = createLiveView({
mount: (socket) => {
socket.assign({
useEmoji: false,
});
setTimeout(()=>socket.sendInfo("from mount->setTimeout"),0);
},
handleEvent(event, socket) {
socket.assign({ useEmoji: !socket.context.useEmoji });
},
handleInfo: (info, socket) => {
console.log({info});
},
render: (context /* const */) => {
const msg = context.useEmoji ? "hello" : "Hello World";
return html`
${msg}
<br/>
<button phx-click="toggle">Toggle message</button>
`;
}
});
Looking at onSendInfo() in packages/core/src/server/socket/liveViewManager.ts, it simply adds the message to a queue. There is no code to deliver the message.
There should be some code to force a new lifecycle if no lifecycle is currently running.
Hey @trondsg! Thanks for trying this out. Right now, you need to use the socket.repeat
to run a function on a given interval. Code looks something like:
mount: (socket) => {
if (socket.connected) {
// only start repeating if the socket is connected (i.e. websocket is connected)
socket.repeat(() => {
// send the tick event internally
socket.sendInfo({ type: "tick" });
}, 1000);
}
...
},
Part of the reason, it works this way is because we keep track of the timeout reference so we can "cleanup" after it automatically. I don't love this requirement and agree it could be more idiomatic to support the setTimeout
directly and prob add a lifecycle callback called close
or cleanup
in which the developer can implement the cleanup.
wdyt?
Hi
I need this for a callback called when new data arrives from a database, and when the database connection is made. setTimeout() was just to simplify the example. (socket.repeat() would be similar to setInterval(), not setTimeout() anyway).
I would expect sendInfo to work like this:
private onSendInfo(info: Info<AnyLiveInfo>) {
// if info is a string, wrap it in an object
if (typeof info === "string") {
info = { type: info };
}
// queue info
this._infoQueue.push(info);
// request info handling at next js event loop iteration
if (false === this._hasFiredInfoDeliveryCallback){
this._hasFiredInfoDeliveryCallback = true;
setTimeout(() => {
this._hasFiredInfoDeliveryCallback = false;
// Insert code to deliver every info message (without running render()), then run pending events, finally run render()).
}, 0);
}
}
A close() lifecycle method makes sense.
Hey @trondsg -
Sorry it took me a bit to get back to you. I've refactored the handleInfo
lifecycle in 0.8.*
versions of LiveViewJS to make this work better.
You can now use setTimeout
and setInterval
in mount and there is an option shutdown
method that you can define on your LiveView
that will be called after it is cleaned up.
Check out: Dashboard Example for some code that I think does what you want.