bigskysoftware / htmx

</> htmx - high power tools for HTML
https://htmx.org
Other
38.42k stars 1.31k forks source link

[v1.9.10] 'ws-send' sends message twice when triggered with 'load' #2141

Open zarekr opened 10 months ago

zarekr commented 10 months ago

With ver 1.9.10 I have noticed that, when using ws-send on an element to send to an open websocket connection, using hx-trigger="load", the message is sent twice. For example:

<div ws-connect="/ws/some/url">

<div id="some_id" ws-send hx-trigger="load" hx-vals='{"some_key":"some_val"}'></div>

</div>

In the network pane of browser dev tools, I'll see this twice as a sent message when using v1.9.10 and only once with 1.9.9:

{"some_key": "some_val", "HEADERS":{"HX-Request":"true","HX-Trigger":"some_id", "HX-Trigger-Name":null,"HX-Target":"some_id","HX-Current-URL":"http://127.0.0.1:8000/home"}}    1703969407.3404334

{"some_key": "some_val", "HEADERS":{"HX-Request":"true","HX-Trigger":"some_id", "HX-Trigger-Name":null,"HX-Target":"some_id","HX-Current-URL":"http://127.0.0.1:8000/home"},    1703969407.3404334

As a result the backend route handling the ws message processes it twice.

I'm guessing it is to do with load being fired an additional time but I can't find where this is occurring. Looking at the changes it may be to do with the new readystate stuff.

zarekr commented 10 months ago

bump as I'm still having this issue with 1.9.10 and have reverted to 1.9.9 in the meantime

DaveManders2205 commented 9 months ago

This problem also occurred to me. This is the temporary workaround im using until it gets fixed: hx-trigger="load delay:1ms"

johannesschaffer commented 9 months ago

Doesn't work for me with v1.9.9 or below either. First time I'm using HTMX, so maybe I'm doing something wrong?

<form hx-ws='send' hx-ext="ws" ws-connect="/chat" class="flex items-center gap-2">
        <input placeholder="Type a message" name='message'
johannesschaffer commented 9 months ago

For me the problem was I used the old attribute: hx-ws='send' instead of hx-boost="" ws-send=""

CaliViking commented 8 months ago

I have the same issue. Even setting delay:0s or delay:0.000 causes the same issue, so there must be somewhere in the code where it explicitly checks for the delay being 0 or non-existent.

The parse interval function is interesting: https://github.com/bigskysoftware/htmx/blob/f919c0705182c904a440e3ff4a9687f4d5166c55/src/htmx.js#L149

It creates a delay in milliseconds as a float from the string. It only checks for ms, s, and m. However, it is possible to pass in 0.000001 (1 nanosecond) and it will work (it will not return a duplicate message like 0 does). With this setting, a full round trip takes about 30 milliseconds (typically from 28 to 32).

If I set the delay to 1s, then the round trip takes from 1.1 to 1.9 seconds to return (is this a JavaScript feature?)

If I pass in 1us or 1ns then it will treat it like 1s as it is only checking for ms, and then checking if the last character is s.

Surprisingly, if I pass in 1h, then it just does a floatParse and returns 1ms. The same would be true if I passed in 1d, 1q, 1y or 1asdnaf. However, if I pass in 1 asdnas, then it will treat it as 1 second (1000 ms).

I have not been able to find where the issue is with duplicated send. Someone with a better understanding of the code would likely be able to fix this very quickly.

Would this be a better way to parse the interval?

/**
 * Parses a string representing a time interval and converts it to milliseconds.
 * The string can end with 'ms' (milliseconds), 's' (seconds), or 'm' (minutes).
 * If no unit is specified, the function assumes the input is in milliseconds.
 * 
 * @param {string} str - The time interval string to parse.
 * @returns {number|undefined} The time interval in milliseconds, or undefined if the input is invalid.
 */
function parseInterval(str) {
    if (typeof str !== 'string') {
        return undefined;
    }

    const unitMultipliers = {
        ms: 1,
        s: 1000,
        m: 1000 * 60,
        // Add more units here if necessary
    };

    const unit = str.match(/[a-zA-Z]+$/)?.[0]; // Extract unit
    const number = parseFloat(str);

    // Check if the parsed number is a valid number
    if (isNaN(number)) {
        return undefined;
    }

    // If a unit is found and is valid, multiply by its multiplier. Otherwise, assume milliseconds.
    return unit && unitMultipliers[unit] ? number * unitMultipliers[unit] : number;
}
thiagomagro commented 4 weeks ago

Same behavior here. Load fires ws-send twice.