Repository: reflex-dev/reflex
Pull request: Add path event
Closes #138

I needed this feature so I figured I would try to add it, turn out it didn't require too much effort.

Notes:
- There is a modification in .templates/web/utils/state.js , if I'm getting it right, this file is copied during `pc init`, so I'm not sure how to update it for existing project? I haven't seen a `pc update` command to refresh `.web`?
- I tried to pick an intuitive name for the variable, if it's no good I can change it of course.

EDIT: I need to read slower, I noticed original issue mention a `get_current_page()` method, I'll have to change some things then.

Problem statement:
Access current page within state
Currently within the state event handlers (and computed var functions), we don't have access to the current page. We can add a method to the `pc.State` class called `get_current_page` that returns this info (potentially along with route params, query params, etc.)

Hints from issue discussion:

Commit message: switch to using get_current_page() method instead

Patch to review:
diff --git a/pynecone/.templates/web/utils/state.js b/pynecone/.templates/web/utils/state.js
index b0612df83e3..256f07e79ad 100644
--- a/pynecone/.templates/web/utils/state.js
+++ b/pynecone/.templates/web/utils/state.js
@@ -70,6 +70,9 @@ export const applyDelta = (state, delta) => {
  */
 export const applyEvent = async (event, router, socket) => {
   // Handle special events
+
+  event.router_data = router;
+
   if (event.name == "_redirect") {
     router.push(event.payload.path);
     return;
diff --git a/pynecone/app.py b/pynecone/app.py
index eac25b905e2..70dfd271985 100644
--- a/pynecone/app.py
+++ b/pynecone/app.py
@@ -297,6 +297,8 @@ async def process(app: App, event: Event) -> StateUpdate:
     # Get the state for the session.
     state = app.state_manager.get_state(event.token)
 
+    state.router_data = event.router_data
+
     # Preprocess the event.
     pre = app.preprocess(state, event)
     if pre is not None:
diff --git a/pynecone/event.py b/pynecone/event.py
index 08f5983ffa8..98608152890 100644
--- a/pynecone/event.py
+++ b/pynecone/event.py
@@ -18,6 +18,9 @@ class Event(Base):
     # The event name.
     name: str
 
+    # The routing data where event occured
+    router_data: Dict[str, Any] = {}
+
     # The event payload.
     payload: Dict[str, Any] = {}
 
diff --git a/pynecone/state.py b/pynecone/state.py
index 0c1270865a0..dbc4e3f3f30 100644
--- a/pynecone/state.py
+++ b/pynecone/state.py
@@ -45,6 +45,9 @@ class State(Base, ABC):
     # The set of dirty substates.
     dirty_substates: Set[str] = set()
 
+    # The routing path that triggered the state
+    router_data: Dict[str, Any]
+
     def __init__(self, *args, **kwargs):
         """Initialize the state.
 
@@ -257,6 +260,11 @@ def _set_default_value(cls, prop: BaseVar):
             field.required = False
             field.default = default_value
 
+    @classmethod
+    @functools.lru_cache()
+    def get_current_page(cls) -> str:
+        return cls.router_data["pathname"]
+
     def __getattribute__(self, name: str) -> Any:
         """Get the state var.
 


Code review comments:
diff_hunk: @@ -70,6 +70,9 @@ export const applyDelta = (state, delta) => {
  */
 export const applyEvent = async (event, router, socket) => {
   // Handle special events
+
+  event.router_data = router;
path: pynecone/.templates/web/utils/state.js
text: @user1:
There seems to be many extra fields in `router` - we can subselect to just the ones we need:
```
event.router_data = (({ pathname, query }) => ({ pathname, query }))(router);
```

diff_hunk: @@ -257,6 +260,11 @@ def _set_default_value(cls, prop: BaseVar):
             field.required = False
             field.default = default_value
 
+    @classmethod
+    @functools.lru_cache()
path: pynecone/state.py
text: @user1:
This shouldn't be a classmethod since it applies to the instance of the state, can just be:
```
def get_current_page(self) -> str:
    return self.router_data["pathname"]
```

This also needs a docstring.

diff_hunk: @@ -45,6 +45,9 @@ class State(Base, ABC):
     # The set of dirty substates.
     dirty_substates: Set[str] = set()
 
+    # The routing path that triggered the state
+    router_data: Dict[str, Any]
path: pynecone/state.py
text: @user1:
We can default this to an empty dictionary:
```
router_data: Dict[str, Any] = {}
```

Also, we need to update:
* this file on line 90, add `router_data` to the set of `skip_vars`
* `tests/test_state.py`, on line 151, add `router_data` to the tuple of fields to get the unit test passing