Repository: reflex-dev/reflex

Issue:
[REF-1559] Vars created using f-string and newline characters do not round-trip
**Describe the bug**
The internal routine which decodes in-band `rx.Var` metadata does not handle newlines correctly.

**To Reproduce**
```python
import reflex as rx


class State(rx.State):
    v: int = 42


def index() -> rx.Component:
    return rx.vstack(
        rx.button("Click me", on_click=rx.call_script(f"\nconsole.log('Hello {State.v}')"))
    )


# Add state and page to the app.
app = rx.App()
app.add_page(index)
```

**Expected behavior**
Clicking the button should print "Hello 42" to the browser console.

Instead it prints

```console
Hello $<reflex.Var>{"state": "state.state", "imports": {"/utils/context": [{"tag": "StateContexts", "is_default": false, "alias": null, "install": true, "render": true}], "react": [{"tag": "useContext", "is_default": false, "alias": null, "install": true, "render": true}]}, "hooks": ["const state__state = useContext(StateContexts.state__state)"]}</reflex.Var>{state__state.v}
```

**Specifics (please complete the following information):**
 - Python Version: 3.11.4
 - Reflex Version: 0.4.0 (main-pre-release)
 - OS: macOS 14.2.1
 - Browser (Optional):
 
**Additional context**
Any regex examining user-supplied strings should use `flags=re.DOTALL` so that `.` metacharacter will match the user-supplied newlines.


<sub>[REF-1559](https://linear.app/reflex-dev/issue/REF-1559/vars-created-using-f-string-and-newline-characters-do-not-round-trip)</sub>


Discussion:

Patch:
diff --git a/reflex/vars.py b/reflex/vars.py
index 49b395ac724..2f31f11908f 100644
--- a/reflex/vars.py
+++ b/reflex/vars.py
@@ -209,7 +209,11 @@ def _decode_var(value: str) -> tuple[VarData | None, str]:
     var_datas = []
     if isinstance(value, str):
         # Extract the state name from a formatted var
-        while m := re.match(r"(.*)<reflex.Var>(.*)</reflex.Var>(.*)", value):
+        while m := re.match(
+            pattern=r"(.*)<reflex.Var>(.*)</reflex.Var>(.*)",
+            string=value,
+            flags=re.DOTALL,  # Ensure . matches newline characters.
+        ):
             value = m.group(1) + m.group(3)
             try:
                 var_datas.append(VarData.parse_raw(m.group(2)))


Test patch:
diff --git a/tests/test_var.py b/tests/test_var.py
index 8e7358bf59d..a6b9deeae30 100644
--- a/tests/test_var.py
+++ b/tests/test_var.py
@@ -638,9 +638,20 @@ def test_extract_state_from_container(value, expect_state):
     assert Var.create_safe(value)._var_state == expect_state
 
 
-def test_fstring_roundtrip():
-    """Test that f-string roundtrip carries state."""
-    var = BaseVar.create_safe("var")._var_set_state("state")
+@pytest.mark.parametrize(
+    "value",
+    [
+        "var",
+        "\nvar",
+    ],
+)
+def test_fstring_roundtrip(value):
+    """Test that f-string roundtrip carries state.
+
+    Args:
+        value: The value to create a Var from.
+    """
+    var = BaseVar.create_safe(value)._var_set_state("state")
     rt_var = Var.create_safe(f"{var}")
     assert var._var_state == rt_var._var_state
     assert var._var_full_name_needs_state_prefix