I’ve submitted this as an issue (#818) but wanted to share it here in case it rings any bells with anyone.
It might be related to #816.
I have a callback that is responsible for adding or removing “jobs” from a list.
It has two inputs and a state. The first input is an Upload, if it is not None then the files are added to the list of jobs. The second input is a button and the state is the value of a Dropdown with multi set to true. If the button is pressed then the jobs from the Dropdown are removed from the list.
When a file is dropped into the Upload, the callback fires and the file is added to the list of jobs.
When the button is pressed, it turns out that the previously uploaded filename is still present in the filenames
Input variable so it ends up getting re-added. If that file had been chosen for deletion then it is both deleted and added, resulting in no-change. If a different file had been chosen for deletion that the first file is added to the list again, resulting in a duplication.
I can work around this by checking the context to see what triggered the callback, rather than depending on the state of the Input variables.
I AssUMe-ed that once the callback ran the Inputs/States related to the Upload would be cleared or reset or … Am I mistaken?
It’s possible that this also explains Issue #816; perhaps the Upload component or Dropzone is seeing a file that is the same as the current contents of those variables and therefor does nothing.
There is a full example of this behavior in this gist. Here’s the callback:
@app.callback(
Output("jobs", "data"),
[
Input("upload-data", "filename"),
Input("delete-jobs-button", "n_clicks"),
],
[State("jobs-to-delete", "value"), State("jobs", "data")],
)
def update_jobs(new_jobs, button, delible_jobs, jobs):
ctx = dash.callback_context
print("---")
print(f"ctx.triggered: {ctx.triggered}")
print(f"Delible: {delible_jobs}")
print(f"New: {new_jobs}")
print(f"Jobs: {jobs}")
if new_jobs is None and delible_jobs is None:
raise PreventUpdate
jobs = jobs or []
if delible_jobs is not None:
jobs = [j for j in jobs if j not in delible_jobs]
if new_jobs is not None:
jobs += new_jobs
return jobs