floodfx / liveviewjs

LiveView-based library for reactive app development in NodeJS and Deno

Home Page:https://liveviewjs.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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.