plotly / dash

Data Apps & Dashboards for Python. No JavaScript Required.
https://plotly.com/dash
MIT License
21.25k stars 2.05k forks source link

[BUG] dict type id causes error with set_props #2935

Closed CRSilkworth closed 2 months ago

CRSilkworth commented 2 months ago

Describe your context Please provide us your environment, so we can easily reproduce the issue.

Describe the bug the new dash_clientside.set_props seems to work great except when trying to use the dict id format. when running the example code:

import dash
from dash import html, dcc
from dash.dependencies import Input, Output

app = dash.Dash(__name__)
app.layout = html.Div(
    [
        html.Div(id="output"),
        dcc.Store(id={"key": "store"}, data=""),
        # dcc.Store(id="store", data=""),
    ]
)

app.clientside_callback(
    """
    function(storeId) {
        function stringifyId(id) {
            if (typeof id !== 'object') {
                return id;
            }
            const stringifyVal = (v) => (v && v.wild) || JSON.stringify(v);
            const parts = Object.keys(id)
                .sort()
                .map((k) => JSON.stringify(k) + ':' + stringifyVal(id[k]));
            return '{' + parts.join(',') + '}';
        }

        storeId=stringifyId(storeId);
        console.log(storeId);

        document.addEventListener('keydown', function(event) {
            const allowedKeys = ['ArrowLeft', 'ArrowRight', 'Enter'];
            if (allowedKeys.includes(event.key)) {
                dash_clientside.set_props(storeId, {data: event.key})
            }
        });
        return window.dash_clientside.no_update;
    }
    """,
    Output({"key": "store"}, "id"),
    # Output("store", "id"),
    Input({"key": "store"}, "id"),
    # Input("store", "id"),
)

@app.callback(
    Output("output", "children"),
    Input({"key": "store"}, "data"),
    # Input("store", "data"),
    prevent_initial_call=True,
)
def handle_key_press(last_key_data):
    print(last_key_data)
    if last_key_data is None:
        raise dash.no_update
    last_key_pressed = last_key_data

    return f"Last key pressed: {last_key_pressed}"

if __name__ == "__main__":
    app.run_server(debug=True)

And a valid key is pressed (ArrowLeft, ArrowRight, Enter), the console logs this error:

TypeError: Cannot read properties of undefined (reading 'concat')
    at getInputHistoryState (reducer.js:57:36)
    at reducer.js:82:34
    at reducer.js:121:16
    at dispatch (redux.js:288:1)
    at index.js:20:1
    at Object.set_props (clientsideFunctions.ts:10:9)
    at HTMLDocument.<anonymous> ((index):57:33)

However if you uncomment the lines with the id='store' and comment out the lines with the dict ids, you get no errors.

Expected behavior Might be doing something stupid on my side but if not set_props should work with both types of id formats.

AnnMarieW commented 2 months ago

Hi @CRSilkworth

Dash already stringifies the dict ids. Try changing your clientside callback to this (it worked for me :slightly_smiling_face: )


app.clientside_callback(
    """
    function(storeId) {    
        document.addEventListener('keydown', function(event) {
            const allowedKeys = ['ArrowLeft', 'ArrowRight', 'Enter'];
            if (allowedKeys.includes(event.key)) {
                dash_clientside.set_props(storeId, {data: event.key})
            }
        });
        return window.dash_clientside.no_update;
    }
    """,
    Output({"key": "store"}, "id"),
    Input({"key": "store"}, "id"),
)
CRSilkworth commented 2 months ago

@AnnMarieW I confirmed this works. Thanks for the quick help!