Open smessmer opened 9 months ago
Another use case: Say the server has a list of data items and either the whole list can change (by adding/removing items) or individual items can change. The reactive system on the client solves this (somewhat) nicely if you do
let list : Signal<Vec<Signal<DataItem>>> = ...;
If a single data item changes, only that signal updates, if new items get added or removed, the whole list changes. That means a single item changing won't require the client to re-render other items. Unfortunately, adding or removing items will likely cause a full re-render, but it's still better than the alternative of re-rendering for every individual item change.
The same scenario would be useful for server sent events, so that the server only needs to send changes to the client and doesn't have to resent the whole list each time. But this might mean that the stream approach I proposed above falls short. I'm not sure how well sending streams nested in streams would work. So we might actually benefit from a more "reactive" approach, i.e. using a shared Signal
instance between the server and the client, even for just the Server -> Client
use case.
Yet another improvement this would have over leptos_sse or leptos_server_events is that those crates can only start the stream once the client is fully loaded and has done another roundtrip to the server to establish the SSE or WebSocket connection. This means this uses "sync rendering" and loses the optimization of "out of order streaming" as defined here where the server starts generating the value already on the first page load.
See also #1284, as there's a lot of overlap here.
To be clear: the reactive system does run on the server. create_effect
does not, but create_isomorphic_effect
does. (This isn't the case in SolidJS, but it is in Leptos.)
Overall, I'm pretty wary of baking an idea like this into the framework because I think it makes it much too easy for users to create really bad architectures for sharing that data.
Signals have the ability to be mutated in place. This is especially useful in situations like the ones you mention--chat or document editing--where cloning and replacing the whole object is quite expensive.
But mutating an object in place over a network connection/websocket isn't possible. So you can either
Doing 2 correctly using either operational transformation or CRDTs is genuinely hard, and an entire field of computing with many possibilities and opinions. Naively you can just implement an update function and a Msg type and send Msgs over the network, but doing this for multiple users will immediately become a train wreck of race conditions.
This is a situation where it shouldn't really be very hard to integrate some OT/CRDT library with the reactive system, but I'm hesitant to pull an opinionated solution like that into the core of the framework.
Your point re: HTML streaming is a good one; the solution here would be for a library to use a resource under the hood, at least for its initial value.
Signals that are read-only on the client would be fine, no? They can then be a building block for CRDTs, efficiently creating the sockets and communicating changes back to the clients, while the framework leaves it up to the developer to implement the client->server communication and any synchronization. Well, maybe that's not necessarily ideal, but at least they can improve on leptos_sse
with better integration into the framework.
EDIT: Maybe this isn't so necessary with the new server functions, at least when using WebSockets over SSE.
Is your feature request related to a problem? Please describe. For web apps that are used by multiple users at the same time, it is useful to have shared state on the server that is immediately synced with all clients. The reactive system seems like a good fit for this, but it currently doesn't run on the server, making things complicated. This feature would be very useful for a variety of web apps, e.g. live chats, but also simple CRUD apps that want to allow concurrent or collaborative editing.
Describe the solution you'd like
Server -> Client only
If we restrict this feature to signals created on the server and streamed to the client, then this shouldn't require too many changes to leptos. During SSR, or later in an even handler for a user interaction, a component could initialize a server side stream that gets synced to a client side signal. The semantics of this would be very similar to
create_resource
, just that it keeps updating the resource whenever the server side stream has a new value available instead of re-running the resource fetcher based on a client side trigger. Something like the following:Client -> Server
It could also be useful to have signals that are written to by the client and automatically streamed to the server. Doing this would likely require much deeper changes to leptos though since afaik, the server currently doesn't run a reactive system. But if we do run a reactive system on the server, collaborative editing becomes very easy to implement. All it needs is for the server to pass a shared
RwSignal
with the data to all the clients, and they can concurrently modify it, and its state is automatically synced to the server and from there to other clients.Describe alternatives you've considered
Server -> Client
, there's leptos_sse and leptos_server_signal but both require additional per-stream setup. For each such stream, developers have to write a customhandle_sse
function and register it withaxum
. Would be nice if this was instead integrated into the#[server]
functionality of leptos so adding a new stream doesn't require changing themain.rs
setup code.Server -> Client
, there's an isomorphy example but that also seems to require per-stream changes tomain.rs
.Client -> Server
, I don't think there's any alternatives yet