Repository: plotly/dash

Issue:
[BUG] allow_duplicate not working with clientside_callback


Thanks for allow_duplicate, it's a very nice addition. 
Everything works well, except for **clientside_callback**. Support for clientside_callback was supposed to be? When adding allow_duplicate, an error occurs:

![image](https://user-images.githubusercontent.com/123159110/225808171-d79aa734-3efe-4915-8a62-1392d9af0433.png)

If you specify several ouputs(with and without allow_duplicate), then even though there will be an error, the value will be updated for ouput without allow_duplicate, but not for output with allow_duplicate, example:

![image](https://user-images.githubusercontent.com/123159110/225808553-cfe999aa-3919-4461-9414-2cb5f41405e8.png)

Tell me, am I doing something wrong? Thank you very much.

Full code sample (with one callback, but I think this is enough to show the error):

```python
import dash

from dash import Dash, html, Input, Output

app = Dash(__name__)
app.layout = html.Div(
    children=[
        html.Div(
            children=['Last pressed button: ', html.Span(id='span', children='empty')]
        ),
        html.Button(
            id='button-right',
            children='right'
        )
    ]
)

# NOT WORKING FOR "SPAN", BUT WARKING FOR BUTTON-RIGHT
dash.clientside_callback(
    """
    function(n_clicks){
        return ["right", `right ${n_clicks}`];
    }
    """,
    [
        Output('span', 'children', allow_duplicate=True),
        Output('button-right', 'children')
    ],
    Input('button-right', 'n_clicks'),
    prevent_initial_call=True
)

# WORKING EXAMPLE TO UNDERSTAND HOW IT SHOULD BE (THE ONLY DIFFERENCE IS THAT NO ALLOW_DUPLICATE)
# dash.clientside_callback(
#     """
#     function(n_clicks){
#         return ["right", `right ${n_clicks}`];
#     }
#     """,
#     [
#         Output('span', 'children'),
#         Output('button-right', 'children')
#     ],
#     Input('button-right', 'n_clicks'),
#     prevent_initial_call=True
# )

if __name__ == '__main__':
    app.run_server(debug=True, port=2414)
```

_Originally posted by @FatHare in https://github.com/plotly/dash/issues/2414#issuecomment-1473088764_
            


Discussion:

Resolving patch:
diff --git a/dash/_callback.py b/dash/_callback.py
--- a/dash/_callback.py
+++ b/dash/_callback.py
@@ -1,4 +1,5 @@
 import collections
+import uuid
 from functools import wraps
 
 import flask
@@ -530,18 +531,14 @@ def register_clientside_callback(
     # If JS source is explicitly given, create a namespace and function
     # name, then inject the code.
     if isinstance(clientside_function, str):
-
-        out0 = output
-        if isinstance(output, (list, tuple)):
-            out0 = output[0]
-
-        namespace = f"_dashprivate_{out0.component_id}"
-        function_name = out0.component_property
+        namespace = "_dashprivate_clientside_funcs"
+        # Just make sure every function has a different name if not provided.
+        function_name = uuid.uuid4().hex
 
         inline_scripts.append(
             _inline_clientside_template.format(
-                namespace=namespace.replace('"', '\\"'),
-                function_name=function_name.replace('"', '\\"'),
+                namespace=namespace,
+                function_name=function_name,
                 clientside_function=clientside_function,
             )
         )


Test patch:
diff --git a/tests/integration/test_patch.py b/tests/integration/test_patch.py
--- a/tests/integration/test_patch.py
+++ b/tests/integration/test_patch.py
@@ -420,3 +420,35 @@ def on_click(_):
 
     dash_duo_mp.wait_for_element("#click2").click()
     dash_duo_mp.wait_for_text_to_equal("#output", "click 2")
+
+
+def test_pch005_clientside_duplicate(dash_duo):
+    app = Dash(__name__)
+
+    app.layout = html.Div(
+        [
+            html.Button("Click 1", id="click1"),
+            html.Button("Click 2", id="click2"),
+            html.Div("initial", id="output"),
+        ]
+    )
+
+    app.clientside_callback(
+        "function() { return 'click1';}",
+        Output("output", "children", allow_duplicate=True),
+        Input("click1", "n_clicks"),
+        prevent_initial_call=True,
+    )
+    app.clientside_callback(
+        "function() { return 'click2';}",
+        Output("output", "children", allow_duplicate=True),
+        Input("click2", "n_clicks"),
+        prevent_initial_call=True,
+    )
+    dash_duo.start_server(app)
+
+    dash_duo.find_element("#click1").click()
+    dash_duo.wait_for_text_to_equal("#output", "click1")
+
+    dash_duo.find_element("#click2").click()
+    dash_duo.wait_for_text_to_equal("#output", "click2")