Repository: reflex-dev/reflex
Pull request: add pc.route decorator
### All Submissions:

- [x] Have you followed the guidelines stated in [CONTRIBUTING.md](https://github.com/pynecone-io/pynecone/blob/main/CONTRIBUTING.md) file?
- [x] Have you checked to ensure there aren't any other open [Pull Requests](https://github.com/pynecone-io/pynecone/pulls ) for the desired changed?

### Type of change

Please delete options that are not relevant.

- [x] New feature (non-breaking change which adds functionality)
- [x] This change requires a documentation update

### New Feature Submission:

- [x] Does your submission pass the tests? 
- [x] Have you linted your code locally prior to submission?

### Changes To Core Features:

- [x] Have you added an explanation of what your changes do and why you'd like us to include them?
- [ ] Have you written new tests for your core changes, as applicable?
- [x] Have you successfully ran tests with your changes locally?

### **After** these steps, you're ready to open a pull request.

You can now decorate any function that return a Pynecone Component with `@pc.route`

`pc.route` accept the same argument as `add_page`.

Decorated functions still need to at least be imported in your main pynecone file.

Closes #462


Problem statement:
Add wrapper for adding a page
Adding a function wrapper for adding a page is more pythonic and would be cleaner instead of dumping all your add_page's at the bottom.
Also don't remove the actual add_page function, just add an alternate wrapper method.

Hints from issue discussion:

Commit message: remove unused import

Patch to review:
diff --git a/pynecone/__init__.py b/pynecone/__init__.py
index b0fcb441f5a..56e66b33f52 100644
--- a/pynecone/__init__.py
+++ b/pynecone/__init__.py
@@ -13,6 +13,7 @@
 from .event import EventChain, console_log, redirect, window_alert
 from .middleware import Middleware
 from .model import Model, session
+from .route import route
 from .state import ComputedVar as var
 from .state import State
 from .style import toggle_color_mode
diff --git a/pynecone/app.py b/pynecone/app.py
index 5e67eaccb46..4e5674e788e 100644
--- a/pynecone/app.py
+++ b/pynecone/app.py
@@ -13,6 +13,7 @@
 from pynecone.event import Event, EventHandler
 from pynecone.middleware import HydrateMiddleware, Middleware
 from pynecone.model import Model
+from pynecone.route import DECORATED_ROUTES
 from pynecone.state import DefaultState, Delta, State, StateManager, StateUpdate
 
 # Define custom types.
@@ -307,6 +308,9 @@ def compile(self, force_compile: bool = False):
         Args:
             force_compile: Whether to force the app to compile.
         """
+        for render, kwargs in DECORATED_ROUTES:
+            self.add_page(render, **kwargs)
+
         # Get the env mode.
         config = utils.get_config()
         if config.env != constants.Env.DEV and not force_compile:
diff --git a/pynecone/route.py b/pynecone/route.py
new file mode 100644
index 00000000000..9bb089c0593
--- /dev/null
+++ b/pynecone/route.py
@@ -0,0 +1,42 @@
+"""The route decorator and associated variables."""
+
+DECORATED_ROUTES = []
+
+
+def route(route=None, title=None, image=None, description=None, on_load=None):
+    """Decorate a function as a page.
+
+    pc.App() will automatically call add_page() for any method decorated with route
+    when App.compile is called.
+
+    Note: the decorated functions still need to be imported
+
+    Args:
+        route (_type_, optional): The route to reach the page. Defaults to None.
+        title (_type_, optional): The title of the page. Defaults to None.
+        image (_type_, optional): The favicon of the page. Defaults to None.
+        description (_type_, optional): The description of the page. Defaults to None.
+        on_load (_type_, optional): The event handler called when the page load. Defaults to None.
+
+    Returns:
+        The decorated function.
+    """
+
+    def decorator(render_fn):
+        kwargs = {}
+        if route:
+            kwargs["route"] = route
+        if title:
+            kwargs["title"] = title
+        if image:
+            kwargs["image"] = image
+        if description:
+            kwargs["description"] = description
+        if on_load:
+            kwargs["on_load"] = on_load
+
+        DECORATED_ROUTES.append((render_fn, kwargs))
+
+        return render_fn
+
+    return decorator


Code review comments:
diff_hunk: @@ -0,0 +1,42 @@
+"""The route decorator and associated variables."""
+
+DECORATED_ROUTES = []
+
+
+def route(route=None, title=None, image=None, description=None, on_load=None):
+    """Decorate a function as a page.
+
+    pc.App() will automatically call add_page() for any method decorated with route
+    when App.compile is called.
+
+    Note: the decorated functions still need to be imported
+
+    Args:
+        route (_type_, optional): The route to reach the page. Defaults to None.
path: pynecone/route.py
text: @user1:
Let's add the type annotations in the function signature and remove them from the docstrings

@author:
sure