Share data between dash components in different modules

Hello,

I want to write my code for my Dashboard in a more organised way, so I put different components in separate Python modules.

Specifically, the app layout is generated by create_layout() in layout.py, which is then assigned to app.layout:

def create_layout(app: Dash) -> html.Div():

    layout = dbc.Container(
        [
            header.render_header(app),

            dbc.Row(
                [
                    dbc.Col(
                        input_side_bar.render_input_side_bar(app),
                        width=dict(size=4)
                    ),
                    dbc.Col(
                        data_display_window.render_window(app),
                        width=dict(size=8)
                    )
                ]
            )
        ]
    )

    return layout

The create_layout() function calls render_header() in header.py, render_input_side_bar() in input_side_bar.py, and render_window() in data_display_window.py; these functions all return html.Div() objects. input_side_bar.render_input_side_bar() takes user input which will then be processed and presented by data_display_window.render_window().

I want to write my code such that different components (e.g., input_side_bar and data_display_window) can communicate inside the create_layout() function, but I’m not sure about the proper way of transferring data between them. For example, I can define some callbacks in the input_side_bar.py module to handle the user inputs, but those inputs can only be shared among components in that file (e.g., with dcc.Store()), right?

I hope my description of the question is clear, but if not, I can add more details.

Thanks
Cory

What kind of data sharing are you looking for? Is it data sharing as a result of user interaction in your dashboard? For example, after the user clicks something on your dashboard and then through a series of callbacks you update various parts of your dashboard? In that case, the best way to share data between callbacks is to use a dcc.Store. One callback can write data there, and then other callbacks can use that data by having State('my_store', 'data') as a callback input.

Note: Initial dashboard setup when loading up your tool for the first time also fits in this flow.

If you want to share fixed configuration data around, then perhaps it’s possible to extract that data from individual callbacks and put that in a separate file that each of your other modules can import.

With respect to callbacks, the callbacks can use any component defined your dashboard. Not only the components defined in the file where the callback resides. As an example, in my tools I usually have a single file that contains all my callbacks. That file will only contain the callbacks and no component. My components will be defined in other modules.

If you want to understand why this works, think about it this way. A callback is a way how you tell your tool how certain components interact. If you have a callback input Input('my_component', 'my_property') then you actually tell your tool that there is a component somewhere with the name 'my_component' that has a property with the name 'my_property' and whenever that changes, then this callback needs to be activated. Note carefully that the names are strings and not references to any particular object! As such, you can (mentally) completely isolate callbacks from the rest of your code, which is what I do when collecting all my callbacks in a separate file.
When your tool loads, Dash will evaluate the callback and start looking around to see if there exists a component with the name my_component. If it doesn’t, you will get an error that you referenced a non-existing component. (The same holds for Output and State of course). Normally, the reason to put the callbacks in separate modules is to keep them close to the components that are affected by the callbacks, so you can keep the code cleaner.

Closing note: In your case, it might also be necessary to revisit the design of your code. If you have tight coupling between different modules, why not merge them together? Or work with functions that accept input parameters which can then be passed in via the central file (layout.py) - i.e. have data_display_window.render_window(app) accept more input parameters. But this all really depends on what data it is exactly that you want to pass around. If it is data due to user interactions, then usually the callback chains will work fine.

Hi Tobs,
Thank you for your detailed answers; it’s very informative.

The data passed around is solely user interactions (from inputs and dropdowns), and I realised that you can actually refer to ids (in callbacks) of components that are defined in other files — I think Dash checks over all files for ids and somehow solves relations between components and callbacks under the hood, when the app is started.

Cheers

I realised that you can actually refer to ids (in callbacks) of components that are defined in other files

Exactly my point. You just put the id’s in the callback and that’s how the connection to the components is made. This is why you can call any component from anywhere in a callback.

I think Dash checks over all files for ids and somehow solves relations between components and callbacks under the hood, when the app is started.

If you want to get a better understanding of what happens when a Dash app loads, please check the page on the Dash app lifecycle. In short, during initialization all callbacks are loaded in your app. It will then execute all callbacks that have inputs currently in the app layout, unless explicitly prevented. If you have a single-page app and you are not creating components with callbacks, this means that at this stage you have access to all components. This is why the callback will work, irrespective of where you defined the callback in associated components in your code.

Note: For multi-page apps or when you create components with callbacks, you need to take some additional steps to make the dashboard work. Check the documentation on multi-page apps for more info.