Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] TypeError: Cannot read properties of undefined (reading 'concat') in getInputHistoryState #2916

Closed
wKollendorf opened this issue Jul 8, 2024 · 2 comments

Comments

@wKollendorf
Copy link

wKollendorf commented Jul 8, 2024

Context
The following code is a simplified example of the real application. The layout initially contains a root div and inputs. By using pattern matching and set_props the callback function can be setup in a general manner. Anyway, when triggering the callback by a button the root div will be populated by any other content like another div and more inputs. The newly created inputs then trigger the same callback and the layout is further updated. A nested structure.

The ID is like this: {'type': 'ELEMENT_TYPE', 'eid': 'ELEMNET_ID, 'target': TARGET_ID'}

  • type: This is the elmenet type (Div, Button, Input, ..)
  • eid: Unique element id
  • target: In case the element is an input, than this is the id of the target element (optional)

In the following example works like this:

  1. Button1 is clicked
  2. Callback update is triggered
  3. key target is checked
  4. run set_props on the element with eid = target and the desired content

failing example:
example

This works for Button1 as expected. But when clicking Button2 (which was created by Button1) the mentioned TypeError occures. When inspecting the console, it can be seen, that the itempath is undefined.

from dash import Dash, html, dcc, ALL, callback_context, exceptions, set_props, callback
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State

# app
app = Dash(__name__)

# layout
app.layout = html.Div([
    html.H4(['Title'], id={'type': 'H4','eid': 'title', 'target': ''})
    ,dbc.Button(['BTN1 (create new div)'], id={'type': 'Button','eid': 'btn1', 'target': 'content1'})
    ,html.Div([], id={'type': 'Div','eid': 'content1', 'target': ''})
    ,html.Div([], id={'type': 'Div','eid': 'content3', 'target': ''})
], id={'type': 'Div','eid': 'body', 'target': ''})

#callback
@callback(
    Output({'type': 'H4', 'eid': 'title', 'target': ''}, 'children')
    ,State({'type': 'Div', 'eid': 'body', 'target': ''}, 'children')
    ,Input({'type': 'Button', 'eid': ALL, 'target': ALL}, 'n_clicks')
    ,Input({'type': 'Input', 'eid': ALL, 'target': ALL}, 'value')
    ,prevent_initial_call=True
)
def update(
    H4__children
    ,Button__n_clicks
    ,Input__value
):
    if callback_context.triggered_id is not None:
        id_changed = callback_context.triggered_id
    else:
        raise exceptions.PreventUpdate

    if id_changed['target'] == 'content1':
        set_props(
            {'type': 'Div', 'eid': id_changed.target, 'target': ''}
            ,{'children': [
                    dbc.Button(['BTN2 (write OUTPUT to new div)'], id={'type': 'Button','eid': 'btn2', 'target': 'content2'})
                    #dbc.Button(['BTN2 (write OUTPUT to new div)'], id={'type': 'Button','eid': 'btn2', 'target': 'content3'})
                    ,html.Div(['div content2 from button1'], id={'type': 'Div','eid': 'content2', 'target': ''})
             ]}
        )

    elif id_changed['target'] == 'content2':
        set_props({'type': 'Div', 'eid': id_changed.target, 'target': ''}, {'children': 'OUTPUT for content2 from button2'})

    elif id_changed['target'] == 'content3':
        set_props({'type': 'Div', 'eid': id_changed.target, 'target': ''}, {'children': 'OUTPUT for content3 from button2'})        
    return 'Title'

# main
if __name__ == '__main__':
    app.run(debug=True, dev_tools_hot_reload=True, dev_tools_ui=True)

Changing the target of Button1 to content3 works (just replace the line for dbc:Button within the callback). So the itempath for Button2 is found and the callback for the new input works.

working example:
fail

pip list | grep dash

dash                      2.17.1
dash_ag_grid              31.2.0
dash-bootstrap-components 1.6.0
dash-core-components      2.0.0
dash-html-components      2.0.0
dash-table                5.0.0
  • OS: Win11
  • Browser chrome
  • Version 126

Describe the bug
The itempath for content2 Div is undefined and concat in getInputHistoryState fails. The itempath is undefined because the component is not present within the store. So it looks like set_props does not update the store?

reducer.js:57 Uncaught (in promise) 
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 dispatch (redux.js:691:1)
    at callbacks.ts:214:9
    at index.js:16:1
    at dispatch (redux.js:691:1)
    at callbacks.ts:255:13

Expected behavior

The message 'OUTPUT for content2 from button2' should appear in Div with id {'type': 'Div','eid': 'content2', 'target': ''}.

@wKollendorf wKollendorf changed the title [BUG] TypeError: Cannot read properties of undefined (reading 'concat') Jul 8, 2024
@wKollendorf
Copy link
Author

Just learnd, that set_props in a callback only modifies the properties of components on the client side. Also new components are only available on the client side.
So this issue can be closed. Anyway, is there a way to make set_props work on server side too?

@gvwilson
Copy link
Contributor

gvwilson commented Jul 26, 2024

Hi - I am closing this as a bug report but @Coding-with-Adam is there a way to make set_props work on the server side?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants