dash icon indicating copy to clipboard operation
dash copied to clipboard

[BUG] dcc.Location not honoring refresh=False for dynamic layouts starting with dash v 1.13

Open giftculture opened this issue 4 years ago • 8 comments

Thank you so much for helping improve the quality of Dash!

We do our best to catch bugs during the release process, but we rely on your help to find the ones that slip through.

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

dash                         1.13.0
dash-auth                    1.3.2
dash-bootstrap-components    0.10.1a0
dash-core-components         1.10.0
dash-daq                     0.1.7
dash-html-components         1.0.3
dash-renderer                1.5.0
dash-table                   4.8.0
dash-table-experiments       0.6.0
dash.ly                      0.17.3

Describe the bug Starting with dash v 1.13, dcc.Location isn't respecting refresh=False for dynamic layouts, it is getting triggered over and over again

index.py

@app.callback(Output(‘page-content’, ‘children’),
[Input(‘url’, ‘pathname’)])
def display_page(pathname):
     if pathname == ‘/apps/cumulative_pnl’:
          return cumulative_pnl.return_layout()

cumulative_pnl.py looks like:

from dash.dependencies import Input, Output, State
import dash_html_components as html
import dash_core_components as dcc
import datetime

from . import render_graphs

from app import app



def return_layout():
    return html.Div(
        [
            datetime.datetime.now(),
            dcc.Location(id='url', refresh=False),
        ],
    )

In Dash 1.13, it seems to get stuck in an infinite loop, triggering over and over, with the datetime continually updating. If I comment out dcc.Location, I get the expected behavior, so there's something going on with dcc.Location - it appears to not be respecting refresh=False Expected behavior

Layout renders one time, with the current datetime (works under dash 1.12) Each time I go to /apps/cumulative_pnl, the return_layout function is invoked and my layout renders once, showing the datetime as of page load

giftculture avatar Jul 27 '20 20:07 giftculture

@giftculture - Thanks for reporting! It is possible for you to create a very simple, standalone example that reproduces this issue? We haven't seen this issue come up yet and our tests didn't fail, so we're not quite sure where this issue might lie yet.

chriddyp avatar Jul 28 '20 15:07 chriddyp

Hello!

A simple example is included here, let me know if you have any issues reading it simple_reload_test.tar.zip

giftculture avatar Jul 29 '20 15:07 giftculture

@chriddyp Not sure if you got notification for my post above, but I attached a zipped tar file that includes a simple example

giftculture avatar Aug 03 '20 16:08 giftculture

Has this been addressed at all or is it still a bug in 1.19.0? I think my callback is basically going into an infinite loop.

halljoshr avatar Mar 02 '21 19:03 halljoshr

Also experiencing a similar issue, page reloads on every callback firing and resets layout to initial state.

jimenezj8 avatar Apr 02 '21 04:04 jimenezj8

Any update on the issue . Is there a work around ?

sjillidimudi avatar Nov 05 '21 15:11 sjillidimudi

I think the issue is with dcc.Location(id='url', refresh=False), in sample_example.py . When you are rendering a specific layout based on the URL path , if we include dcc.Location again its causing the issue . The refresh=False is not being respected . Currently i removed the dcc.Location in all my sub layouts and things are working as expected . Its not a solution though but if some one struck and can live with this new adjustment , this can be helpful.

sjillidimudi avatar Nov 10 '21 09:11 sjillidimudi

I'll note after experimenting a bit that the infinite loop bug is only triggered if the id attribute of the dcc.Location class is set to 'url' - it doesn't occur if the attribute is set to a different string than 'url'

def return_layout():
    return html.Div(
        [
            datetime.datetime.now(),
            #dcc.Location(id='url', refresh=False), # broken
            dcc.Location(id='url_blah', refresh=False),  # works
        ],
    )

Also, this bug is still present in Dash 2.0.0

giftculture avatar Dec 14 '21 12:12 giftculture

Here is another minimal example If the id of the dcc.Location in page2 layout is the same as the dcc.Location in app.layout, then page 2 is called repeatedly

from dash import Dash, dcc, html, Input, Output, callback
import datetime

page1 = html.Div("PAGE 1 CONTENT")


def page2():
    return html.Div(
        [
            datetime.datetime.now(),
            dcc.Location(id="url", refresh=False),
            # If the dcc.Location has a different id, this page is not called repeatedly
            # dcc.Location(id='url2', refresh=False),
        ],        
    )


app = Dash(__name__, suppress_callback_exceptions=True)

app.layout = html.Div(
    [
        dcc.Location(id="url", refresh=False),
        dcc.Link("Go to Page 1", href="/page1"),
        html.Br(),
        dcc.Link("Go to Page 2", href="/page2"),
        html.Div(id="page-content"),
    ]
)


@callback(Output("page-content", "children"), Input("url", "pathname"))
def display_page(pathname):
    if pathname == "/page1":
        return page1
    elif pathname == "/page2":
        return page2()
    elif pathname == "/":
        return page1
    else:
        return "404"


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

AnnMarieW avatar Feb 26 '23 01:02 AnnMarieW

Here is another related issue. If dcc.Location is in one of the pages rather than in app.py, navigation can be buggy


"""

Try alternating between pg1 and pg2 by clicking on the links
Try it with dcc.Location only in page2, then try it with dcc.Location only in app.py

"""
from dash import Dash, dcc, html, Input, Output, register_page, page_container

page1 = html.Div("PAGE 1 CONTENT")

page2 = html.Div(
    [
        html.Div("Page 2"),
        # Navigation is buggy when dcc.Location is here
        dcc.Location(id="url", refresh=False),
        html.Div( id="pg2-output"),
    ],
)


app = Dash(__name__, suppress_callback_exceptions=True, use_pages=True, pages_folder="")

register_page("page2", layout=page2)
register_page("page1", path="/", layout=page1)

app.layout = html.Div(
    [
        # Navigation works as expected when dcc.Location is in app.py
       # dcc.Location(id="url", refresh=False),
        dcc.Link("Go to Page 1", href="/"),
        html.Br(),
        dcc.Link("Go to Page 2", href="/page2"),
        html.Div(page_container)
    ]
)


@app.callback(
    Output("pg2-output", "children"),
    Input("url", "href")
)
def update(href):
    return href


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


AnnMarieW avatar Feb 26 '23 17:02 AnnMarieW

Thanks for providing the MWE @AnnMarieW . I am also seeing this bug in a larger project and was not able to isolate it so far, the MWE helps a lot.

Has there been any progress on finding what causes this? I tried to look into it but my understanding of react is limited.

My current working hypothesis is that _dashprivate_path of the Location component is incorrect. When updating the Location props with a new href that prop gets assigned to something elese (e.g the letter "A" below), leading to an error like this:

Error: An object was provided as `children` instead of a component, string, or number (or list of those). Check the children property that looks something like:
{
  "0": "P",
  "1": {
    "0": "A",
    "props": {
      "pathname": "/page2",
      "href": "http://127.0.0.1:8050/page2"
    }
  },
  "2": "G",
  "3": "E",
  "4": " ",
  "5": "1",
  "6": " ",
  "7": "C",
  "8": "O",
  "9": "N",
  "10": "T",
  "11": "E",
  "12": "N",
  "13": "T"
}
    at validateComponent (TreeContainer.ts:55:15)
    ....

But maybe this deserves it's own bug/issue. There is no problem with refreshing as in this title. Also - while i am not an expert on dash - I am quite sure the label good first issue is not really applicable here.

And it basically breaks navigation when using the pages feature.

jowlo avatar Mar 28 '23 07:03 jowlo