I’ve been trying to troubleshoot this issue, but I think I might have a fundamental misunderstanding of what is expected and maybe what I’m seeing is what is expected.
I assign a list of rows where each row is made up of a list of maps that represent the individual cells. Everything looks good in the UI, and updates as expected, but when I enableDebug on the liveSocket it looks like the entire interior of the table is being sent down the websocket when any change is made, not just the changed cell. It does properly breakup the static content with the dynamic content, but sends redundant data.
The reason I think the issue is my understanding is because I inspected the code and websocket communication on the LiveDashboard system on the Sockets page and basically see the same result as with my code. There is a table, and everytime the 15s update goes out, it looks like it is sending all the cells of the table, even the unchanged cells.
Here is the shortest version of my code. I dynamically adjust the number of columns and rows based on the screen size by using a phx-hook, here is the HTML:
<div id="page_resize" phx-hook="PageResize"></div>
here is the hook in app.js:
Hooks.PageResize = {
mounted() {
window.addEventListener("resize", (e) => {
this.pushEvent("page-resize", {
screenWidth: window.innerWidth,
screenHeight: window.innerHeight,
});
});
},
destroyed() {
// TODO: create this.pageResize in mounted so it can be destroyed
// window.removeEventListener("resize", this.pageResize);
},
};
and that event is handled by:
def handle_event(
"page-resize",
%{"screenWidth" => screenWidth, "screenHeight" => screenHeight},
socket
) do
socket =
assign(
socket,
screenWidth: screenWidth,
screenHeight: screenHeight,
rows: Table.Cell.list_rows(screenWidth, screenHeight)
)
{:noreply, socket}
end
which populates the “rows” with this list_rows function:
def list_rows(width, height) do
rows = Integer.floor_div(height - 2, 32)
cols = Integer.floor_div(width - 2, 132)
for r <- 1..rows do
for c <- 1..cols do
%{
row: r,
column: c,
string: "R#{r}C#{c}"
}
end
end
end
and finally in the render I have this code:
<table>
<%= for row <- @rows do %>
<tr>
<%= for cell <- row do %>
<td class="bg-gray-50 border-gray-200 border-2">
<div
tabindex="0"
class="font-mono w-32 overflow-hidden whitespace-nowrap pl-1 py-0.5 text-gray-700"
>
<%= cell.string %>
</div>
</td>
<% end %>
</tr>
<% end %>
</table>
When the window size changes enough to add a new row or column (or remove one), I would expect that only the new rows would come through the diff. But instead every cell comes through on the websocket update. Not just the innerText (like “R1C1”) but also the lengthy TD & DIV definition with all of the same classes and such.
Am I doing something wrong, or is this what I should expect?
I will say that the updates are super fast, but I’m doing local development and I’m not quite sure what it will be like for people with higher latency and lower bandwidth. So if I am doing something wrong to cause it to send so much data, I want to resolve it.
Elixir 1.11.2, Phoenix 1.5.7, OTP 23, LiveView 0.15.0