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] dcc.Location not honoring refresh=False for dynamic layouts starting with dash v 1.13 #1346

Closed
giftculture opened this issue Jul 27, 2020 · 11 comments · Fixed by #2489
Closed

Comments

@giftculture
Copy link

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

@chriddyp
Copy link
Member

chriddyp commented Jul 28, 2020

@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.

@giftculture
Copy link
Author

Hello!

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

@giftculture
Copy link
Author

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

@halljoshr
Copy link

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.

@jimenezj8
Copy link

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

@sjillidimudi
Copy link

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

@sjillidimudi
Copy link

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.

@giftculture
Copy link
Author

giftculture commented Dec 14, 2021

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

@AnnMarieW
Copy link
Collaborator

AnnMarieW commented Feb 26, 2023

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
Copy link
Collaborator

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)

@jowlo
Copy link
Contributor

jowlo commented Mar 28, 2023

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 added a commit to jowlo/dash that referenced this issue Mar 31, 2023
Currently, if the component is not mounted (e.g. because on a different page),
the component will still receive the change event and the `onLocationChange`
callback changes its props via `this.props.setProps` with the itempath from
('_dashprivate_path') in the DOM. The object, however, is
not on the dom and a random other DOM element is assigned the changed props.

The PR deregisters the event listeners of the component is unmounted. They will
be added again via `componentDidMount` anyways if the component is mounted again.

fixes plotly#1346
jowlo added a commit to jowlo/dash that referenced this issue Apr 11, 2023
Currently, if the component is not mounted (e.g. because on a different page),
the component will still receive the change event and the `onLocationChange`
callback changes its props via `this.props.setProps` with the itempath from
('_dashprivate_path') in the DOM. The object, however, is
not on the dom and a random other DOM element is assigned the changed props.

The PR deregisters the event listeners of the component is unmounted. They will
be added again via `componentDidMount` anyways if the component is mounted again.

fixes plotly#1346
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants