Repository: plotly/dash

Issue:
[BUG] Inline clientside_callback scripts not found in Dash 2.9.2
**Environment**
```
dash                 2.9.2
```

**Describe the bug**

In Dash 2.9.2, the clientside callbacks defined inline get a random function name which seems to break. Note: the issue solves itslef when refreshing the page, however every first load triggers this issue. Tested in 2.9.1 and this does not happen, it was introduced with the random function names in 2.9.2.

Using a deterministic function name instead solves the issue:

```python
import itertools
COUNTER = itertools.count()

def register_clientside_callback(...):
    ...

    increment = next(COUNTER)
    function_name = f"inline-script-{increment:06d}"
    ...
```

**Expected behavior**

The clientside_callbacks defined inline should work from the first load.

**Screenshots**

![image](https://user-images.githubusercontent.com/20662281/229090259-de16ddce-8273-449c-9ad7-fc6c2c0d164b.png)




Discussion:

Resolving patch:
diff --git a/dash/_callback.py b/dash/_callback.py
--- a/dash/_callback.py
+++ b/dash/_callback.py
@@ -1,5 +1,5 @@
 import collections
-import uuid
+import hashlib
 from functools import wraps
 
 import flask
@@ -534,8 +534,8 @@ def register_clientside_callback(
     # name, then inject the code.
     if isinstance(clientside_function, str):
         namespace = "_dashprivate_clientside_funcs"
-        # Just make sure every function has a different name if not provided.
-        function_name = uuid.uuid4().hex
+        # Create a hash from the function, it will be the same always
+        function_name = hashlib.md5(clientside_function.encode("utf-8")).hexdigest()
 
         inline_scripts.append(
             _inline_clientside_template.format(


Test patch:
diff --git a/tests/integration/clientside/test_clientside_restarts.py b/tests/integration/clientside/test_clientside_restarts.py
new file mode 100644
--- /dev/null
+++ b/tests/integration/clientside/test_clientside_restarts.py
@@ -0,0 +1,55 @@
+import pytest
+from dash import Dash, html, Output, Input
+
+
+@pytest.mark.skip(reason="Hot-reload & clientside callbacks doesn't work properly")
+def test_clrs001_clientside_inline_restarts(dash_duo_mp):
+    # FIXME find another way to test clientside callbacks restarts
+    reloads = 0
+
+    def create_app():
+        nonlocal reloads
+
+        app = Dash(__name__)
+
+        app.layout = html.Div([
+            html.Button("Click", id="click"),
+            html.Div(id="output"),
+            html.Div(reloads, id="reload")
+        ])
+
+        app.clientside_callback(
+            "(n_clicks) => `clicked ${n_clicks}`",
+            Output("output", "children"),
+            Input("click", "n_clicks"),
+            prevent_initial_call=True
+        )
+        reloads += 1
+        return app
+
+    hot_reload_settings = dict(
+        dev_tools_hot_reload=True,
+        dev_tools_ui=True,
+        dev_tools_serve_dev_bundles=True,
+        dev_tools_hot_reload_interval=0.1,
+        dev_tools_hot_reload_max_retry=100,
+    )
+
+    dash_duo_mp.start_server(
+        create_app(),
+        **hot_reload_settings
+    )
+    dash_duo_mp.find_element("#click").click()
+    dash_duo_mp.wait_for_text_to_equal("#output", "clicked 1")
+
+    dash_duo_mp.server.stop()
+
+    dash_duo_mp.start_server(
+        create_app(),
+        navigate=False,
+        **hot_reload_settings
+    )
+    dash_duo_mp.wait_for_text_to_equal("#reload", "1")
+    dash_duo_mp.find_element("#click").click()
+    # reloaded so 1 again.
+    dash_duo_mp.wait_for_text_to_equal("#output", "clicked 1")