Skip to content

"Maximum update depth exceeded" error by updating the initialData of the dash-excalidraw component through re-keying its parent. #3

Open
@pip-install-python

Description

@pip-install-python
import dash
from dash import html, dcc, Input, Output, State
import dash_excalidraw
from dash.exceptions import PreventUpdate
import uuid
from datetime import datetime
import random

# Initialize the Dash app
app = dash.Dash(__name__, suppress_callback_exceptions=True)

# Initial empty canvas data (can be simplified further)
def get_initial_canvas_data(content="Initial"):
    return {
        'type': 'excalidraw',
        'version': 2,
        'source': 'Minimal-Bug-Reproducer',
        'elements': [
            {
                "id": str(uuid.uuid4()),
                "type": "text",
                "x": 50 + random.randint(-20, 20), # Slightly change position
                "y": 50 + random.randint(-20, 20),
                "width": 300,
                "height": 50,
                "angle": 0,
                "strokeColor": "#000000",
                "backgroundColor": "transparent",
                "fillStyle": "hachure",
                "strokeWidth": 1,
                "strokeStyle": "solid",
                "roughness": 1,
                "opacity": 100,
                "text": f"{content} - {datetime.now().time()}", # Change text content
                "fontSize": 20,
                "fontFamily": 1,
                "textAlign": "left",
                "verticalAlign": "top",
                "baseline": 18,
                "seed": random.randint(1, 1000000), # Change seed
                "version": 1,
                "versionNonce": random.randint(1, 1000000), # Change versionNonce
                "isDeleted": False,
                "updated": int(datetime.now().timestamp() * 1000)
            }
        ],
        'appState': {
            'gridSize': 20,
            'viewBackgroundColor': '#ffffff',
            "theme": "light" # Ensure theme is present, as it's often in appState
        },
        'files': {}
    }

initial_data_for_load = get_initial_canvas_data("Loaded at Start")

# Define the app layout
app.layout = html.Div([
    html.H1("Dash Excalidraw Bug Reproducer"),
    html.Button("Update Excalidraw with New Data", id="update-button", n_clicks=0),
    html.Div(id="status-output", style={"marginTop": "10px", "marginBottom": "10px"}),
    html.Div(
        id="excalidraw-wrapper-container", # This div's children will be updated
        children=html.Div( # The re-keyed div
            key='excalidraw-initial-key',
            children=dash_excalidraw.DashExcalidraw(
                id='excalidraw-instance',
                width='100%',
                height='500px',
                initialData=initial_data_for_load,
            ),
            style={"border": "1px solid lightgrey", "height": "500px"}
        )
    ),
    html.P("Open your browser's developer console (usually F12) to check for 'Maximum update depth exceeded' errors after clicking the button multiple times.")
])

@app.callback(
    Output("excalidraw-wrapper-container", "children"),
    Output("status-output", "children"),
    Input("update-button", "n_clicks"),
    prevent_initial_call=True
)
def update_excalidraw(n_clicks):
    if n_clicks == 0:
        raise PreventUpdate

    print(f"Button clicked: {n_clicks}")
    new_content = f"Update #{n_clicks}"
    new_excalidraw_data = get_initial_canvas_data(new_content)

    # Create the new DashExcalidraw component instance
    new_excalidraw_component = dash_excalidraw.DashExcalidraw(
        id='excalidraw-instance', # ID can remain the same as React handles identity by key
        width='100%',
        height='500px',
        initialData=new_excalidraw_data,
    )

    # Create a new wrapper div with a new unique key to force remount
    # Using a timestamp for the key is a reliable way to make it unique
    new_key = f'excalidraw-key-{datetime.now().timestamp()}'
    print(f"Generated new key: {new_key}")

    new_wrapper_div = html.Div(
        key=new_key,
        children=new_excalidraw_component,
        style={"border": "1px solid lightblue", "height": "500px"} # Visual cue for change
    )

    status_message = f"Excalidraw updated with new data at {datetime.now().strftime('%H:%M:%S')}. New key: {new_key}"
    print(status_message)

    return new_wrapper_div, status_message


if __name__ == '__main__':
    print("Starting Dash app on http://127.0.0.1:8050/")
    print("After clicking the button, check the browser's developer console for errors.")
    app.run(debug=True, port=8250)

Metadata

Metadata

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions