diff --git a/reflex/.templates/jinja/web/pages/_app.js.jinja2 b/reflex/.templates/jinja/web/pages/_app.js.jinja2 index 40e31dee6..ee3e24540 100644 --- a/reflex/.templates/jinja/web/pages/_app.js.jinja2 +++ b/reflex/.templates/jinja/web/pages/_app.js.jinja2 @@ -38,13 +38,13 @@ export default function MyApp({ Component, pageProps }) { }, []); return ( <ThemeProvider defaultTheme={ defaultColorMode } attribute="class"> - <AppWrap> - <StateProvider> - <EventLoopProvider> - <Component {...pageProps} /> - </EventLoopProvider> - </StateProvider> - </AppWrap> + <StateProvider> + <EventLoopProvider> + <AppWrap> + <Component {...pageProps} /> + </AppWrap> + </EventLoopProvider> + </StateProvider> </ThemeProvider> ); } diff --git a/reflex/.templates/web/utils/state.js b/reflex/.templates/web/utils/state.js index 93c664ef1..1eeb4e64a 100644 --- a/reflex/.templates/web/utils/state.js +++ b/reflex/.templates/web/utils/state.js @@ -301,10 +301,7 @@ export const applyEvent = async (event, socket) => { // Send the event to the server. if (socket) { - socket.emit( - "event", - event, - ); + socket.emit("event", event); return true; } @@ -497,7 +494,7 @@ export const uploadFiles = async ( return false; } - const upload_ref_name = `__upload_controllers_${upload_id}` + const upload_ref_name = `__upload_controllers_${upload_id}`; if (refs[upload_ref_name]) { console.log("Upload already in progress for ", upload_id); @@ -833,6 +830,13 @@ export const useEventLoop = ( } })(); } + + // Cleanup function. + return () => { + if (socket.current) { + socket.current.disconnect(); + } + }; }); // localStorage event handling diff --git a/reflex/app.py b/reflex/app.py index 9fe0f2992..3ba1ff53f 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -54,6 +54,7 @@ from reflex.compiler.compiler import ExecutorSafeFunctions, compile_theme from reflex.components.base.app_wrap import AppWrap from reflex.components.base.error_boundary import ErrorBoundary from reflex.components.base.fragment import Fragment +from reflex.components.base.strict_mode import StrictMode from reflex.components.component import ( Component, ComponentStyle, @@ -943,6 +944,12 @@ class App(MiddlewareMixin, LifespanMixin): # If a theme component was provided, wrap the app with it app_wrappers[(20, "Theme")] = self.theme + # Get the env mode. + config = get_config() + + if config.react_strict_mode: + app_wrappers[(200, "StrictMode")] = StrictMode.create() + should_compile = self._should_compile() for route in self._unevaluated_pages: @@ -977,9 +984,6 @@ class App(MiddlewareMixin, LifespanMixin): + adhoc_steps_without_executor, ) - # Get the env mode. - config = get_config() - # Store the compile results. compile_results = [] diff --git a/reflex/components/base/strict_mode.py b/reflex/components/base/strict_mode.py new file mode 100644 index 000000000..46b01ad87 --- /dev/null +++ b/reflex/components/base/strict_mode.py @@ -0,0 +1,10 @@ +"""Module for the StrictMode component.""" + +from reflex.components.component import Component + + +class StrictMode(Component): + """A React strict mode component to enable strict mode for its children.""" + + library = "react" + tag = "StrictMode" diff --git a/reflex/utils/prerequisites.py b/reflex/utils/prerequisites.py index 8330a315c..e79963dc7 100644 --- a/reflex/utils/prerequisites.py +++ b/reflex/utils/prerequisites.py @@ -912,7 +912,6 @@ def _update_next_config( next_config = { "basePath": config.frontend_path or "", "compress": config.next_compression, - "reactStrictMode": config.react_strict_mode, "trailingSlash": True, "staticPageGenerationTimeout": config.static_page_generation_timeout, } diff --git a/tests/units/test_app.py b/tests/units/test_app.py index 074e7f2ef..cf49f9d5c 100644 --- a/tests/units/test_app.py +++ b/tests/units/test_app.py @@ -1276,12 +1276,23 @@ def compilable_app(tmp_path) -> Generator[tuple[App, Path], None, None]: yield app, web_dir -def test_app_wrap_compile_theme(compilable_app: tuple[App, Path]): +@pytest.mark.parametrize( + "react_strict_mode", + [True, False], +) +def test_app_wrap_compile_theme( + react_strict_mode: bool, compilable_app: tuple[App, Path], mocker +): """Test that the radix theme component wraps the app. Args: + react_strict_mode: Whether to use React Strict Mode. compilable_app: compilable_app fixture. + mocker: pytest mocker object. """ + conf = rx.Config(app_name="testing", react_strict_mode=react_strict_mode) + mocker.patch("reflex.config._get_config", return_value=conf) + app, web_dir = compilable_app app.theme = rx.theme(accent_color="plum") app._compile() @@ -1292,24 +1303,37 @@ def test_app_wrap_compile_theme(compilable_app: tuple[App, Path]): assert ( "function AppWrap({children}) {" "return (" - "<RadixThemesColorModeProvider>" + + ("<StrictMode>" if react_strict_mode else "") + + "<RadixThemesColorModeProvider>" "<RadixThemesTheme accentColor={\"plum\"} css={{...theme.styles.global[':root'], ...theme.styles.global.body}}>" "<Fragment>" "{children}" "</Fragment>" "</RadixThemesTheme>" "</RadixThemesColorModeProvider>" - ")" + + ("</StrictMode>" if react_strict_mode else "") + + ")" "}" ) in "".join(app_js_lines) -def test_app_wrap_priority(compilable_app: tuple[App, Path]): +@pytest.mark.parametrize( + "react_strict_mode", + [True, False], +) +def test_app_wrap_priority( + react_strict_mode: bool, compilable_app: tuple[App, Path], mocker +): """Test that the app wrap components are wrapped in the correct order. Args: + react_strict_mode: Whether to use React Strict Mode. compilable_app: compilable_app fixture. + mocker: pytest mocker object. """ + conf = rx.Config(app_name="testing", react_strict_mode=react_strict_mode) + mocker.patch("reflex.config._get_config", return_value=conf) + app, web_dir = compilable_app class Fragment1(Component): @@ -1341,8 +1365,7 @@ def test_app_wrap_priority(compilable_app: tuple[App, Path]): ] assert ( "function AppWrap({children}) {" - "return (" - "<RadixThemesBox>" + "return (" + ("<StrictMode>" if react_strict_mode else "") + "<RadixThemesBox>" '<RadixThemesText as={"p"}>' "<RadixThemesColorModeProvider>" "<Fragment2>" @@ -1352,8 +1375,7 @@ def test_app_wrap_priority(compilable_app: tuple[App, Path]): "</Fragment2>" "</RadixThemesColorModeProvider>" "</RadixThemesText>" - "</RadixThemesBox>" - ")" + "</RadixThemesBox>" + ("</StrictMode>" if react_strict_mode else "") + ")" "}" ) in "".join(app_js_lines) diff --git a/tests/units/test_prerequisites.py b/tests/units/test_prerequisites.py index 3bd029077..4723d8648 100644 --- a/tests/units/test_prerequisites.py +++ b/tests/units/test_prerequisites.py @@ -32,7 +32,7 @@ runner = CliRunner() app_name="test", ), False, - 'module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true, staticPageGenerationTimeout: 60};', + 'module.exports = {basePath: "", compress: true, trailingSlash: true, staticPageGenerationTimeout: 60};', ), ( Config( @@ -40,7 +40,7 @@ runner = CliRunner() static_page_generation_timeout=30, ), False, - 'module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true, staticPageGenerationTimeout: 30};', + 'module.exports = {basePath: "", compress: true, trailingSlash: true, staticPageGenerationTimeout: 30};', ), ( Config( @@ -48,7 +48,7 @@ runner = CliRunner() next_compression=False, ), False, - 'module.exports = {basePath: "", compress: false, reactStrictMode: true, trailingSlash: true, staticPageGenerationTimeout: 60};', + 'module.exports = {basePath: "", compress: false, trailingSlash: true, staticPageGenerationTimeout: 60};', ), ( Config( @@ -56,7 +56,7 @@ runner = CliRunner() frontend_path="/test", ), False, - 'module.exports = {basePath: "/test", compress: true, reactStrictMode: true, trailingSlash: true, staticPageGenerationTimeout: 60};', + 'module.exports = {basePath: "/test", compress: true, trailingSlash: true, staticPageGenerationTimeout: 60};', ), ( Config( @@ -65,14 +65,14 @@ runner = CliRunner() next_compression=False, ), False, - 'module.exports = {basePath: "/test", compress: false, reactStrictMode: true, trailingSlash: true, staticPageGenerationTimeout: 60};', + 'module.exports = {basePath: "/test", compress: false, trailingSlash: true, staticPageGenerationTimeout: 60};', ), ( Config( app_name="test", ), True, - 'module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true, staticPageGenerationTimeout: 60, output: "export", distDir: "_static"};', + 'module.exports = {basePath: "", compress: true, trailingSlash: true, staticPageGenerationTimeout: 60, output: "export", distDir: "_static"};', ), ], )