Description
When creating more complex Dash apps, I've found that the function signatures of callbacks is somewhat limiting. More specifically:
Inputs and States are all combined together into the one list of positional parameters (ie *args
) and you have to keep track of how many there of each in order to identify them from the args list. For a handful of Inputs/States, this is manageable, however if you're programmatically generating a large number of them (eg I recently made an app with 1600 Inputs -- granted this was more of a form than a reactive-viz) then this becomes trickier.
In addition to keeping tabs on how many Inputs/States are being provided, in a larger app, you need to start managing the logic of how they are handled, as this can't be done explicitly for each input. My solution to this was to create a helper Store class which allowed me to to associate---for each input component that can be targeted by an Input/State---some of the handling logic while I'm constructing the layout, then retrieving the handling logic from this Store within the callback. The friction involved in this is again the simple positional arguments of callback functions, and having to associate them with the element IDs that have been added to the Store. I worked around this by having a method in the store to generate a list of Inputs for the callback signature and one to generate list of corresponding metadata dicts, then iterating over a those two lists zipped together:
@app.callback(Output('target', 'children'), store.get_inputs(), [Event('submit', 'click')])
def submit_form(*values):
# do stuff
for value, data in zip(values, store.get_data()):
# do stuff with each value, using the corresponding information in data on how to handle it
return "Done"
If the callback signatures contained more information about each Input/State/Event, and in a manner more accessible, then these difficulties could be much lessened. For example, something along the lines of callback(states, inputs, events)
where each positional argument is a dictionary---keyed perhaps by element_id.property---with values containing dicts which hold the component registration information for the relevant components involved in the callback eg: element_id, property, value.
The important thing is that you be able to iterate over the callback's Inputs, States, and Events, and be able to readily access the associated element ID so that you can look it up in your own data structure. So those positional args could still just be lists of component registrations with the needed metadata, but the advantage of using dicts is that you could quickly index the registration you need without having to worry about where it was defined in the @app.callback decorator. (I guess that might would prompt the question of why the decorator needs to be lists then also)
I do however like the simplicity of the current callback signature, and for many simple apps it could be preferable. One option might be to create a more expressive callback function alongside the simple one that people can choose to use only when the situation calls for. Another compromise I see is to keep using only positional parameters, but have a flag in the callback decorator which toggles whether the arguments are a list of values or a list of {element_id, property, value} dicts.
See also this discussion in the community forums, where someone was running into the limitation of not being able to ascertain which element was the source of a click
Event.