You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/src/reference/hooks.md
+54-22
Original file line number
Diff line number
Diff line change
@@ -309,13 +309,13 @@ This hook utilizes the Django's authentication framework in a way that provides
309
309
310
310
The `#!python channels.auth.*` functions cannot trigger re-renders of your ReactPy components. Additionally, they do not provide persistent authentication when used within ReactPy.
311
311
312
-
Django's authentication design requires cookies to retain login status. ReactPy is rendered via WebSockets, and browsers do not allow active WebSocket connections to modify cookies.
312
+
This is a result of Django's authentication design, which requires cookies to retain login status. ReactPy is rendered via WebSockets, and browsers do not allow active WebSocket connections to modify cookies.
313
313
314
314
To work around this limitation, when `#!python use_auth().login()` is called within your application, ReactPy performs the following process...
315
315
316
-
1. The server authenticates the user into the WebSocket session
317
-
2. The server generates a temporary login token linked to the WebSocket session
318
-
3. The server commands the browser to fetch the login token via HTTP
316
+
1. The server authenticates the user into the WebSocket
317
+
2. The server generates a temporary login token
318
+
3. The server commands the browser to use the login token (via HTTP)
319
319
4. The client performs the HTTP request
320
320
5. The server returns the HTTP response, which contains all necessary cookies
321
321
6. The client stores these cookies in the browser
@@ -352,7 +352,7 @@ Shortcut that returns the WebSocket or HTTP connection's `#!python User`.
352
352
353
353
Store or retrieve a `#!python dict` containing arbitrary data specific to the connection's `#!python User`.
354
354
355
-
This hook is useful for storing user-specific data, such as preferences, settings, or any generic key-value pairs.
355
+
This hook is useful for storing user-specific data, such as preferencesor settings.
356
356
357
357
User data saved with this hook is stored within the `#!python REACTPY_DATABASE`.
358
358
@@ -397,7 +397,7 @@ User data saved with this hook is stored within the `#!python REACTPY_DATABASE`.
397
397
398
398
### Use Channel Layer
399
399
400
-
Subscribe to a [Django Channels layer](https://channels.readthedocs.io/en/latest/topics/channel_layers.html) to send/receive messages.
400
+
Subscribe to a [Django Channels Layer](https://channels.readthedocs.io/en/latest/topics/channel_layers.html) to communicate messages.
401
401
402
402
Layers are a multiprocessing-safe communication system that allows you to send/receive messages between different parts of your application.
403
403
@@ -415,30 +415,28 @@ This is often used to create chat systems, synchronize data between components,
415
415
416
416
| Name | Type | Description | Default |
417
417
| --- | --- | --- | --- |
418
-
| `#!python name` | `#!python str | None` | The name of the channel to subscribe to. If you define a `#!python group_name`, you can keep `#!python name` undefined to auto-generate a unique name. | `#!python None` |
419
-
| `#!python group_name` | `#!python str | None` | If configured, any messages sent within this hook will be broadcasted to all channels in this group. | `#!python None` |
420
-
| `#!python group_add` | `#!python bool` | If `#!python True`, the channel will automatically be added to the group when the component mounts. | `#!python True` |
421
-
| `#!python group_discard` | `#!python bool` | If `#!python True`, the channel will automatically be removed from the group when the component dismounts. | `#!python True` |
422
-
| `#!python receiver` | `#!python AsyncMessageReceiver | None` | An async function that receives a `#!python message: dict` from a channel. If more than one receiver waits on the same channel name, a random receiver will get the result. | `#!python None` |
423
-
| `#!python layer` | `#!python str` | The channel layer to use. This layer must be defined in `#!python settings.py:CHANNEL_LAYERS`. | `#!python 'default'` |
418
+
| `#!python channel` | `#!python str | None` | The name of the channel this hook will send/receive messages on. If `#!python group` is defined and `#!python channel` is `#!python None`, ReactPy will automatically generate a unique channel name. | `#!python None` |
419
+
| `#!python group` | `#!python str | None` | If configured, the `#!python channel` is added to a `#!python group` and any messages sent by `#!python AsyncMessageSender` is broadcasted to all channels within the `#!python group`. | `#!python None` |
420
+
| `#!python receiver` | `#!python AsyncMessageReceiver | None` | An async function that receives a `#!python message: dict` from a channel. | `#!python None` |
421
+
| `#!python layer` | `#!python str` | The Django Channels layer to use. This layer must be defined in `settings.py:CHANNEL_LAYERS`. | `#!python 'default'` |
424
422
425
423
<font size="4">**Returns**</font>
426
424
427
425
| Type | Description |
428
426
| --- | --- |
429
-
| `#!python AsyncMessageSender` | An async callable that can send a `#!python message: dict`. |
427
+
| `#!python AsyncMessageSender` | An async callable that can send messages to the channel(s). This callable accepts a single argument, `#!python message: dict`, which is the data sent to the channel or group of channels. |
430
428
431
429
??? warning "Extra Django configuration required"
432
430
433
431
In order to use this hook, you will need to configure Django to enable channel layers.
434
432
435
433
The [Django Channels documentation](https://channels.readthedocs.io/en/latest/topics/channel_layers.html#configuration) has information on what steps you need to take.
436
434
437
-
In summary, you will need to:
435
+
Here is a short summary of the most common installation steps:
438
436
439
437
1. Install [`redis`](https://redis.io/download/) on your machine.
440
438
441
-
2. Run the following command to install `channels-redis` in your Python environment.
439
+
2. Install `channels-redis` in your Python environment.
442
440
443
441
```bash linenums="0"
444
442
pip install channels-redis
@@ -457,23 +455,57 @@ This is often used to create chat systems, synchronize data between components,
457
455
}
458
456
```
459
457
460
-
??? question "How do I broadcast a message to multiple components?"
458
+
??? tip "Learn about the quirks of Django Channel Layers"
459
+
460
+
ReactPy tries to simplify the process of using Django Channels Layers, but it is important to understand how they work.
461
+
462
+
There are a few quirks of Django Channels Layers to be aware of:
463
+
464
+
- Any given `#!python channel` should only have one `#!python receiver` registered to it, under normal circumstances.
465
+
- This is why ReactPy automatically generates a unique channel name when using `#!python group`.
466
+
- When using `#!python group` within this hook, it is suggested to leave `#!python channel` undefined to let ReactPy automatically create a unique channel name (unless you know what you are doing).
467
+
- If you have multiple receivers for the same `#!python channel`, only one receiver will get the result.
468
+
- This quirk extends to groups as well. For example, If you have two component instances that use the same `#!python channel` within a `#!python group`, the message will only reach one receiver (for that channel).
469
+
- Channels exist independently of their `#!python group`.
470
+
- Groups are just a loose collection of channel names where a copy of each message can be sent.
471
+
- As a result, Django allows you to send messages directly to a `#!python channel` even if it is within a `#!python group`.
472
+
- By default, `#!python RedisChannelLayer` will close groups once they have existed for more than 24 hours.
473
+
- You need to create your own subclass of `#!python RedisChannelLayer` to change this behavior.
474
+
- By default, `#!python RedisChannelLayer` only allows 100 messages backlogged within a `#!python channel` receive queue.
475
+
- Rapidly sending messages can overwhelm this queue, resulting in new messages being dropped.
476
+
- If you expect to exceed this limit, you need to create your own subclass of `#!python RedisChannelLayer` to change this behavior.
461
477
462
-
If more than one receiver waits on the same channel, a random one will get the result.
478
+
??? question "How do I broadcast a message to multiple components?"
463
479
464
-
To get around this, you can define a `#!python group_name` to broadcast messages to all channels within a specific group. If you do not define a channel `#!python name` while using groups, ReactPy will automatically generate a unique channel name for you.
480
+
Groups allow you to broadcast messages to all channels within that group. If you do not define a `#!python channel` while using groups, ReactPy will automatically generate a unique channel name for you.
465
481
466
-
In the example below, all messages sent by the `#!python sender` component will be received by all `#!python receiver` components that exist (across every active client browser).
482
+
In the example below, since all components use the same channel `#!python group`, messages sent by `#!python my_sender_component` will reach all existing instances of `#!python my_receiver_component_1` and `#!python my_receiver_component_2`.
467
483
468
484
=== "components.py"
469
485
470
486
```python
471
487
{% include "../../examples/python/use_channel_layer_group.py" %}
472
488
```
473
489
490
+
??? question "How do I send a message to a single component (point-to-point communication)?"
491
+
492
+
The most common way of using `#!python use_channel_layer` is to broadcast messages to multiple components via a `#!python group`.
493
+
494
+
However, you can also use this hook to establish unidirectional, point-to-point communication towards a single `#!python receiver` function. This is slightly more efficient since it avoids the overhead of `#!python group` broadcasting.
495
+
496
+
In the example below, `#!python my_sender_component` will communicate directly to a single instance of `#!python my_receiver_component`. This is achieved by defining a `#!python channel` while omitting the `#!python group` parameter.
497
+
498
+
=== "components.py"
499
+
500
+
```python
501
+
{% include "../../examples/python/use_channel_layer_single.py" %}
502
+
```
503
+
504
+
Note that if you have multiple instances of `#!python my_receiver_component` using the same `#!python channel`, only one will receive the message.
505
+
474
506
??? question "How do I signal a re-render from something that isn't a component?"
475
507
476
-
There are occasions where you may want to signal a re-render from something that isn't a component, such as a Django model signal.
508
+
There are occasions where you may want to signal to the `#!python use_channel_layer` hook from something that isn't a component, such as a Django [model signal](https://docs.djangoproject.com/en/stable/topics/signals/).
477
509
478
510
In these cases, you can use the `#!python use_channel_layer` hook to receive a signal within your component, and then use the `#!python get_channel_layer().send(...)` to send the signal.
479
511
@@ -499,7 +531,7 @@ This is often used to create chat systems, synchronize data between components,
499
531
500
532
### Use Connection
501
533
502
-
Returns the active connection, which is either a Django [WebSocket](https://channels.readthedocs.io/en/stable/topics/consumers.html#asyncjsonwebsocketconsumer) or a [HTTP Request](https://docs.djangoproject.com/en/4.2/ref/request-response/#django.http.HttpRequest).
534
+
Returns the active connection, which is either a Django [WebSocket](https://channels.readthedocs.io/en/stable/topics/consumers.html#asyncjsonwebsocketconsumer) or a [HTTP Request](https://docs.djangoproject.com/en/stable/ref/request-response/#django.http.HttpRequest).
503
535
504
536
=== "components.py"
505
537
@@ -601,7 +633,7 @@ Shortcut that returns the root component's `#!python id` from the WebSocket or H
601
633
602
634
The root ID is a randomly generated `#!python uuid4`. It is notable to mention that it is persistent across the current connection. The `uuid` is reset only when the page is refreshed.
603
635
604
-
This is useful when used in combination with [`#!python use_channel_layer`](#use-channel-layer) to send messages to a specific component instance, and/or retain a backlog of messages in case that component is disconnected via `#!python use_channel_layer( ... , group_discard=False)`.
636
+
This is useful when used in combination with [`#!python use_channel_layer`](#use-channel-layer) to send messages to a specific component instance.
0 commit comments