change logic to check if event handlers actually contain state. Also delay websocket object check in state.js so server side events can get executed for stateless apps

This commit is contained in:
Elijah 2024-06-12 20:02:10 +00:00
parent 7d2a5c238a
commit 6437067de8
4 changed files with 37 additions and 52 deletions

View File

@ -213,6 +213,10 @@ export const applyEvent = async (event, socket) => {
return false; return false;
} }
// Only proceed if the socket is up, otherwise we throw the event into the void
if (!socket) {
return false;
}
// Update token and router data (if missing). // Update token and router data (if missing).
event.token = getToken(); event.token = getToken();
if ( if (
@ -246,6 +250,10 @@ export const applyEvent = async (event, socket) => {
* @returns Whether the event was sent. * @returns Whether the event was sent.
*/ */
export const applyRestEvent = async (event, socket) => { export const applyRestEvent = async (event, socket) => {
// Only proceed if the socket is up, otherwise we throw the event into the void
if (!socket) {
return false;
}
let eventSent = false; let eventSent = false;
if (event.handler === "uploadFiles") { if (event.handler === "uploadFiles") {
@ -282,11 +290,6 @@ export const queueEvents = async (events, socket) => {
* @param socket The socket object to send the event on. * @param socket The socket object to send the event on.
*/ */
export const processEvent = async (socket) => { export const processEvent = async (socket) => {
// Only proceed if the socket is up, otherwise we throw the event into the void
if (!socket) {
return;
}
// Only proceed if we're not already processing an event. // Only proceed if we're not already processing an event.
if (event_queue.length === 0 || event_processing) { if (event_queue.length === 0 || event_processing) {
return; return;

View File

@ -541,9 +541,7 @@ class App(LifespanMixin, Base):
# Ensure state is enabled if this page uses state. # Ensure state is enabled if this page uses state.
if self.state is None: if self.state is None:
if on_load or component._has_event_triggers( if on_load or component._has_stateful_event_triggers():
exclude_event_trigger_values=[constants.ColorMode.TOGGLE]
):
self._enable_state() self._enable_state()
else: else:
for var in component._get_vars(include_children=True): for var in component._get_vars(include_children=True):

View File

@ -7,7 +7,7 @@ import typing
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from functools import lru_cache, wraps from functools import lru_cache, wraps
from hashlib import md5 from hashlib import md5
from types import SimpleNamespace from types import SimpleNamespace, LambdaType
from typing import ( from typing import (
Any, Any,
Callable, Callable,
@ -1113,65 +1113,38 @@ class Component(BaseComponent, ABC):
return vars return vars
def _event_triggers_contains_excluded_values(
self, exclude_event_trigger_values: list[str]
) -> bool:
"""Check if the component's event triggers contain excluded value names.
When an event trigger value is a Var, we check the var name else, if the value is
an EventChain, we go through all the events comparing the function names (or qualname for non-state
event handlers).
Args: def _event_trigger_values_use_state(self) -> bool:
exclude_event_trigger_values: excluded values to check for.
Returns:
If the component's event triggers contain any excluded value
"""
for trigger in self.event_triggers.values(): for trigger in self.event_triggers.values():
if isinstance(trigger, EventChain): if isinstance(trigger, EventChain):
for event in trigger.events: for event in trigger.events:
if ( if event.handler.state_full_name or isinstance(event.handler.fn, LambdaType) and event.handler.fn.__name__== (lambda: None).__name__:
(
event.handler.state_full_name
and event.handler.fn.__name__
in exclude_event_trigger_values
)
or not event.handler.state_full_name
and event.handler.fn.__qualname__
in exclude_event_trigger_values
):
return True return True
elif ( elif isinstance(trigger, Var) and trigger._var_state:
isinstance(trigger, Var)
and trigger._var_name in exclude_event_trigger_values
):
return True return True
return False return False
def _has_event_triggers( def _has_stateful_event_triggers(self):
self, exclude_event_trigger_values: list[str] | None = None
) -> bool:
"""Check if the component or children have any event triggers.
Args: if self.event_triggers and self._event_trigger_values_use_state():
exclude_event_trigger_values: Event trigger var names to exclude from this check. return True
else:
for child in self.children:
if isinstance(child, Component) and child._has_stateful_event_triggers():
return True
return False
def _has_event_triggers(self) -> bool:
"""Check if the component or children have any event triggers.
Returns: Returns:
True if the component or children have any event triggers. True if the component or children have any event triggers.
""" """
if exclude_event_trigger_values is None: if self.event_triggers:
exclude_event_trigger_values = []
if self.event_triggers and not self._event_triggers_contains_excluded_values(
exclude_event_trigger_values
):
return True return True
else: else:
for child in self.children: for child in self.children:
if isinstance(child, Component) and child._has_event_triggers( if isinstance(child, Component) and child._has_event_triggers():
exclude_event_trigger_values
):
return True return True
return False return False

View File

@ -2146,6 +2146,17 @@ class TriggerState(rx.State):
["_console", "_alert"], ["_console", "_alert"],
False, False,
), ),
(
rx.box(
rx.text(
"random text",
on_click=lambda x: x,
),
),
["_console", "_alert"],
False,
),
], ],
) )
def test_has_event_triggers(component, exclude_event_trigger_values, output): def test_has_event_triggers(component, exclude_event_trigger_values, output):