diff --git a/htmltools/_jsx.py b/htmltools/_jsx.py index 15ecea4..e8769e6 100644 --- a/htmltools/_jsx.py +++ b/htmltools/_jsx.py @@ -138,14 +138,29 @@ def tagify_tagifiable_and_get_metadata(x: Any) -> Any: # instead of calling the standard Tag.get_html_string() method to format the # object, we'll recurse using _render_react_js(), which descends into the tree # and formats objects appropriately for inside of a JSX element. + component = _render_react_js(self, 2, "\n") + + # Ideally, we'd use document.currentScript.after() to insert the component + # directly after the script tag, but when dynamically rendered via jQuery (i.e., + # @render_ui()), the script tag actually executes in , which makes the + # document.currentScript reference basically useless for this purpose. So, + # instead, to make this work when dynamically rendered, this script queries for + # the first JSXTag script that hasn't yet rendered, and insert the component + # there. It seems like this should work fine as long as these script tags are + # synchronously executed in order, but if we ever need them to render + # asynchronously, it might make sense to use a unique ID for each script tag + # instead. js = "\n".join( [ "(function() {", " var container = new DocumentFragment();", " ReactDOM.render(", - _render_react_js(self, 2, "\n"), + component, " , container);", - " document.currentScript.after(container);", + " var thisScript = document.querySelector('script[data-needs-render]');", + f" if (!thisScript) throw new Error('Failed to render JSXTag(\"{self.name}\")');", + " thisScript.after(container);", + " thisScript.removeAttribute('data-needs-render');", "})();", ] ) @@ -153,6 +168,7 @@ def tagify_tagifiable_and_get_metadata(x: Any) -> Any: return Tag( "script", type="text/javascript", + data_needs_render=True, children=[ HTML("\n" + js + "\n"), _lib_dependency("react", script={"src": "react.production.min.js"}), diff --git a/tests/test_jsx_tags.py b/tests/test_jsx_tags.py index 96f5a85..a148654 100644 --- a/tests/test_jsx_tags.py +++ b/tests/test_jsx_tags.py @@ -19,13 +19,16 @@ def test_jsx_tags(): - @@ -44,7 +47,7 @@ def test_jsx_tags(): - @@ -78,7 +84,7 @@ def test_jsx_tags(): ) assert str(x) == textwrap.dedent( """\ - """ ) @@ -116,7 +125,7 @@ def test_jsx_tags(): ) assert str(x) == textwrap.dedent( """\ - """ ) @@ -139,7 +151,7 @@ def test_jsx_tags(): ) assert str(x) == textwrap.dedent( """\ - """ ) @@ -159,7 +174,7 @@ def test_jsx_tags(): ) assert str(x) == textwrap.dedent( """\ - """ ) @@ -209,7 +227,7 @@ def tagify(self): assert str(x) == textwrap.dedent( """\ - """ )