Repository navigation
Allow server-side components to send messages to client-side components #975
Description
Activity
Tangentially related
- changed the title
[-]Allow Server-side components to send events to client-side components[/-][+]Allow server-side components to send messages to client-side components[/+]on May 11, 2023 The name
use_channelkind of implies that multiple channels and/or channel groups can be defined per component. Here's some other potential names- use_data_message
- use_message
- use_messenger
- use_message_listener
- use_message_signal
- use_message_subscriber
- use_signal_listener
- use_consumer
- use_data_channel
- use_data_messenger
- use_data_transport
- use_data_dispatcher
- use_communicator
The design below would allow for an event-driven architecture when possible, inspired by
channels.With any design we make, we're going to need to be careful about our asyncio cancellation policy for messengers. This will extend to us thinking deeper about async
use_effect.from dataclasses import dataclass from reactpy import component, hooks @dataclass class DataMessenger: def __init__(self, *args, **kwargs): self.args = args or () self.kwargs = kwargs or {} async def send(self, message): """This method typically isn't overridden.""" ... async def recieve(self, message): """This method typically gets overriden with custom user behavior.""" ... async def register(self, ... ): """This method typically isn't overridden. Used to register a message handler to a component.""" ... def use_messenger(messenger: DataMessenger) -> DataMessenger: """This is a hook that allows users to communicate to client-side components. The given `messenger` will be registered to the current component. """ MESSENGER_CONTEXT: list = hooks.use_context(...) # A given `Messenger` should persist throughout the entire lifecycle of a component if messenger not in MESSENGER_CONTEXT: # Not a usable implementation, but you get the point messenger.register(...) # Update `args`/`kwargs` on each component re-render. else: messenger.args = MESSENGER_CONTEXT[0].args messenger.kwargs = MESSENGER_CONTEXT[0].kwargs # Give the user access to the registered messenger, if he wants to do some shenanigans within `use_effect`. return messenger @component def example(): # If the user only wants to be event driven, then he won't need to save a `messenger = ...` variable messenger = use_messenger(DataMessenger(example_value=1)) @hooks.use_effect async def send_message(): # Using `messenger` methods here would rely on our half-baked async `use_effect` # I think this might be an argument to fleshing out async effects await messenger.send("Hello, World!")
The name
use_channelkind of implies that multiple channels and/or channel groups can be defined per componentI think this is true. One could imagine a server-side component that constructs several channels in order to communicate with different client-side components.
use_messengerwould be fine too though.If we're allowing support for multiple communicators, imo that narrows it down to the following:
- use_channel
- use_data_channel
- use_messenger
- use_data_messenger
- use_communicator
- use_bridge
I updated the example above to better outline how persistence of
DataMessengercan be achieved.- addedrelease-minorWarrents a minor releaseWarrents a minor releaseand removedflag-triageNot prioritized.Not prioritized.
on May 15, 2023
Current Situation
Presently, custom client-side components are able to send events back to the server. However, it is not possible to send events from server-side components to client-side ones.
Proposed Actions
Now that custom JS components have the ability to register callbacks with the client this should be technologically feasible. With that said, while users can listen in on particular message types, there is no concept of a message "target". We could achieve this by having message types of the form
server-event:<the-target>, but this seems like a bit of a hack. Perhaps we can allow (require?) a nullabletargetfield for this purpose.Before diving into all those details though, we need to work out exactly what this interface should look like.
I can imagine having an interface similar to:
where
custom_js_componentwould then subscribe to messages of the typechannel-messageand targetchannel.id.