1
0

hydrate_middleware.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. """Middleware to hydrate the state."""
  2. from __future__ import annotations
  3. from typing import TYPE_CHECKING, Dict, List, Optional, Union
  4. from pynecone import constants
  5. from pynecone.event import Event, EventHandler, get_hydrate_event
  6. from pynecone.middleware.middleware import Middleware
  7. from pynecone.state import State, StateUpdate
  8. from pynecone.utils import format
  9. if TYPE_CHECKING:
  10. from pynecone.app import App
  11. IS_HYDRATED = "is_hydrated"
  12. State.add_var(IS_HYDRATED, type_=bool, default_value=False)
  13. class HydrateMiddleware(Middleware):
  14. """Middleware to handle initial app hydration."""
  15. async def preprocess(
  16. self, app: App, state: State, event: Event
  17. ) -> Optional[Union[StateUpdate, List[StateUpdate]]]:
  18. """Preprocess the event.
  19. Args:
  20. app: The app to apply the middleware to.
  21. state: The client state.
  22. event: The event to preprocess.
  23. Returns:
  24. An optional delta or list of state updates to return.
  25. """
  26. if event.name == get_hydrate_event(state):
  27. route = event.router_data.get(constants.RouteVar.PATH, "")
  28. if route == "/":
  29. load_event = app.load_events.get(constants.INDEX_ROUTE)
  30. elif route:
  31. load_event = app.load_events.get(route.lstrip("/"))
  32. else:
  33. load_event = None
  34. updates = []
  35. # first get the initial state
  36. delta = format.format_state({state.get_name(): state.dict()})
  37. if delta:
  38. updates.append(StateUpdate(delta=delta))
  39. # then apply changes from on_load event handlers on top of that
  40. if load_event:
  41. if not isinstance(load_event, List):
  42. load_event = [load_event]
  43. for single_event in load_event:
  44. updates.append(
  45. await self.execute_load_event(
  46. state, single_event, event.token, event.payload
  47. )
  48. )
  49. # extra message telling the client state that hydration is complete
  50. updates.append(
  51. StateUpdate(
  52. delta=format.format_state({state.get_name(): {IS_HYDRATED: True}})
  53. )
  54. )
  55. return updates
  56. async def execute_load_event(
  57. self, state: State, load_event: EventHandler, token: str, payload: Dict
  58. ) -> StateUpdate:
  59. """Execute single load event.
  60. Args:
  61. state: The client state.
  62. load_event: A single load event to execute.
  63. token: Client token
  64. payload: The event payload
  65. Returns:
  66. A state Update.
  67. Raises:
  68. ValueError: If the state value is None.
  69. """
  70. substate_path = format.format_event_handler(load_event).split(".")
  71. ex_state = state.get_substate(substate_path[:-1])
  72. if not ex_state:
  73. raise ValueError(
  74. "The value of state cannot be None when processing an on-load event."
  75. )
  76. return await state._process_event(
  77. handler=load_event, state=ex_state, payload=payload, token=token
  78. )