3535from reflex .app_mixins import AppMixin , LifespanMixin , MiddlewareMixin
3636from reflex .compiler import compiler
3737from reflex .compiler import utils as compiler_utils
38- from reflex .compiler .compiler import ExecutorSafeFunctions , compile_theme
38+ from reflex .compiler .compiler import (
39+ ExecutorSafeFunctions ,
40+ compile_theme ,
41+ readable_name_from_component ,
42+ )
3943from reflex .components .base .app_wrap import AppWrap
4044from reflex .components .base .error_boundary import ErrorBoundary
4145from reflex .components .base .fragment import Fragment
@@ -284,6 +288,25 @@ class UnevaluatedPage:
284288 meta : list [dict [str , str ]]
285289 context : dict [str , Any ] | None
286290
291+ def merged_with (self , other : UnevaluatedPage ) -> UnevaluatedPage :
292+ """Merge the other page into this one.
293+
294+ Args:
295+ other: The other page to merge with.
296+
297+ Returns:
298+ The merged page.
299+ """
300+ return dataclasses .replace (
301+ self ,
302+ title = self .title if self .title is not None else other .title ,
303+ description = self .description
304+ if self .description is not None
305+ else other .description ,
306+ on_load = self .on_load if self .on_load is not None else other .on_load ,
307+ context = self .context if self .context is not None else other .context ,
308+ )
309+
287310
288311@dataclasses .dataclass ()
289312class App (MiddlewareMixin , LifespanMixin ):
@@ -719,22 +742,37 @@ def add_page(
719742 # Check if the route given is valid
720743 verify_route_validity (route )
721744
722- if route in self ._unevaluated_pages and environment .RELOAD_CONFIG .is_set ():
723- # when the app is reloaded(typically for app harness tests), we should maintain
724- # the latest render function of a route.This applies typically to decorated pages
725- # since they are only added when app._compile is called.
726- self ._unevaluated_pages .pop (route )
745+ unevaluated_page = UnevaluatedPage (
746+ component = component ,
747+ route = route ,
748+ title = title ,
749+ description = description ,
750+ image = image ,
751+ on_load = on_load ,
752+ meta = meta ,
753+ context = context ,
754+ )
727755
728756 if route in self ._unevaluated_pages :
729- route_name = (
730- f"`{ route } ` or `/`"
731- if route == constants .PageNames .INDEX_ROUTE
732- else f"`{ route } `"
733- )
734- raise exceptions .RouteValueError (
735- f"Duplicate page route { route_name } already exists. Make sure you do not have two"
736- f" pages with the same route"
737- )
757+ if self ._unevaluated_pages [route ].component is component :
758+ unevaluated_page = unevaluated_page .merged_with (
759+ self ._unevaluated_pages [route ]
760+ )
761+ console .warn (
762+ f"Page { route } is being redefined with the same component."
763+ )
764+ else :
765+ route_name = (
766+ f"`{ route } ` or `/`"
767+ if route == constants .PageNames .INDEX_ROUTE
768+ else f"`{ route } `"
769+ )
770+ existing_component = self ._unevaluated_pages [route ].component
771+ raise exceptions .RouteValueError (
772+ f"Tried to add page { readable_name_from_component (component )} with route { route_name } but "
773+ f"page { readable_name_from_component (existing_component )} with the same route already exists. "
774+ "Make sure you do not have two pages with the same route."
775+ )
738776
739777 # Setup dynamic args for the route.
740778 # this state assignment is only required for tests using the deprecated state kwarg for App
@@ -746,16 +784,7 @@ def add_page(
746784 on_load if isinstance (on_load , list ) else [on_load ]
747785 )
748786
749- self ._unevaluated_pages [route ] = UnevaluatedPage (
750- component = component ,
751- route = route ,
752- title = title ,
753- description = description ,
754- image = image ,
755- on_load = on_load ,
756- meta = meta ,
757- context = context ,
758- )
787+ self ._unevaluated_pages [route ] = unevaluated_page
759788
760789 def _compile_page (self , route : str , save_page : bool = True ):
761790 """Compile a page.
@@ -1021,9 +1050,11 @@ def _apply_decorated_pages(self):
10211050
10221051 This can move back into `compile_` when py39 support is dropped.
10231052 """
1053+ app_name = get_config ().app_name
10241054 # Add the @rx.page decorated pages to collect on_load events.
1025- for render , kwargs in DECORATED_PAGES [get_config (). app_name ]:
1055+ for render , kwargs in DECORATED_PAGES [app_name ]:
10261056 self .add_page (render , ** kwargs )
1057+ DECORATED_PAGES [app_name ].clear ()
10271058
10281059 def _validate_var_dependencies (self , state : type [BaseState ] | None = None ) -> None :
10291060 """Validate the dependencies of the vars in the app.
0 commit comments