locked
blazor seerver | weird navigation behavior due to nested components RRS feed

  • Question

  • User2110873642 posted

    sometimes when nesting components into each other, the navigation gets buggy, parts of the screen dont update, disposings are not fired, or in some scenarios the anchor links do not work at all anymore, doing only a change to the adress bar at best.

    what explains this behaviour and how can i learn to work with it instead of against it, forcing reloads?

    Monday, August 10, 2020 10:56 PM

Answers

  • User-474980206 posted

    while (if you know what you are doing) instance properties can handle local state, there are a lot of pitfalls, because the tree render does not know about them.

    • components may render more than once during a state update, so local state should never be updated during render. properties can not be updated during render (you can change one, but it will not persist).
    • a component tree render due to state change only needs to re-render components whose state (properties) have changed. the tree render can skip at a component whose properties have not been modified since last render. 
    • if you use local state you will probably need to implement ShouldRender() as only your code knows when it needs to render due to local state changes

    blazor components have limited lifecycle events:

    • OnInitialized - called when added to the tree and initial props set. may be called twice if server pre-render done. component trees often call this mount. while it seems logical, do not do javascript interop init here
    • OnParametersSet - called after the parent set the components properties and before render. Complex logic based on parameter values or async work should be done here. if your component only uses a nested object, for performance you may need to cache and compare to prevent unnecessary renders.
    • OnRender - called during tree render. this is the inline code. supposed to be a very fast generation of html. do not make i/o calls or do complex logic here.  remember may be called more than once per virtual DOM update (as state updates are batched).
    • OnAfterRender - this is called by the virtual DOM after the true DOM has been updated. Used if javascript interop needs to access the true DOM. you can check the first parameter to initial javascript interop event hookups.
    • Dispose - the component is removed from the tree. use this unhook javascript interop event hookups

    remember and state change requires a re-render of the component tree, but only components whose state has been modified will be called. if your component is rendering too often then you probably need to implement ShouldRender(). Common if using nested properties. 

    if your component is not re-rendering when you want, then it is not subscribed  to the proper state.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, August 12, 2020 9:25 PM

All replies

  • User475983607 posted

    sometimes when nesting components into each other, the navigation gets buggy, parts of the screen dont update, disposings are not fired, or in some scenarios the anchor links do not work at all anymore, doing only a change to the adress bar at best.

    what explains this behaviour and how can i learn to work with it instead of against it, forcing reloads?

    I can't reproduce this navigation issue.  Share a code sample the community can run without modification to reproduce the issue. 

    Tuesday, August 11, 2020 12:56 AM
  • User2110873642 posted

    one scenario i know from head:

    add a routeparameter variable, and render it.

    then navigate to the same component, and only change the parameter, it does not update on navigation.

    i ffound a workaround to reset all parameters on parameterchangedasync, but its ugly.

    Tuesday, August 11, 2020 1:01 AM
  • User475983607 posted

    one scenario i know from head:

    add a routeparameter variable, and render it.

    then navigate to the same component, and only change the parameter, it does not update on navigation.

    i ffound a workaround to reset all parameters on parameterchangedasync, but its ugly.

    Rather that trying to decipher your written word, make an effort to share code the community and copy, run, and reproduce the issue without having to guess what you're doing.

    Tuesday, August 11, 2020 1:11 AM
  • User-474980206 posted

    you are probably changing state values at the wrong time (say render), or not triggering a change state.

    blazor works by using a component tree and a virtual dom. component renders update the virtual dom and the virtual dom updates the browser dom.  when state changes, a re-render is performed. for performance you only want to render the components that state changes effects. as blazor walks the component tree, it will only call component render if a state (bound) variable has changed since the last render.

    a common error is a child render really need its parent to render different, but the parent is not bound to the dependent state. 

    Tuesday, August 11, 2020 3:47 PM
  • User2110873642 posted

    mhm.

    and when you nest a component on page A and page B. and you navigate from A to B. the parameters in the nested component are kept, which is annoying to me. does it have to do with the onafterrender? even with parameters in the route, it completely ignores the new routes, which looks to me like it breaks the whole purpose of url routing and parameters to begin with.

    i use onparameterchanged life method to reset the parameters manually, so it fetches the values from the route again.

    am i doing it wrong?

    Tuesday, August 11, 2020 5:13 PM
  • User-474980206 posted

    clearly you are coding state incorrectly. page a and page b should pass the url parameter to nested component as a bound parameter.

    child:

    <div>the child @urlparameter</div>
    
    @code {
        [Parameter]
        public string urlparameter { get; set; }
    }
    

    parent

    @page "/ParentComponent"
    @page "/ParentComponent/{urlparameter}"
    
    <h1>Parent</h1>
    
    <ChildComponent urlparameter="@urlparameter" />
    
    @code {
        [Parameter]
        public string urlparameter { get; set; }
    
    }

    Tuesday, August 11, 2020 8:37 PM
  • User2110873642 posted

    i could not, because i do not understand the problem to such an extend, that i cannot produce a failing code at will.

    Wednesday, August 12, 2020 6:35 PM
  • User2110873642 posted

    after more experimenting it has to do with variables instead of parameters. variables do not go back to init state after navigation.

    how should this be handled?

    for example, take the defauilt /counter.razor. when navigating to the same component, the count does not go back to 0.

    is there a 

    [ResetOnParameterChange] private int Count = 0;

    kinda feature?

    Wednesday, August 12, 2020 6:39 PM
  • User475983607 posted

    State in a Blazor page is like a C# class.   Class fields do not magically reset when accessing another class member.  It is up to you to write code that does what you want when some event fires.

    As requested several times, please share code the community can run to reproduce the issue, steps to reproduce the behavior, and the expected behavior.  I think the problem is your understanding but I'm not sure without context.

    Wednesday, August 12, 2020 7:00 PM
  • User-474980206 posted

    while (if you know what you are doing) instance properties can handle local state, there are a lot of pitfalls, because the tree render does not know about them.

    • components may render more than once during a state update, so local state should never be updated during render. properties can not be updated during render (you can change one, but it will not persist).
    • a component tree render due to state change only needs to re-render components whose state (properties) have changed. the tree render can skip at a component whose properties have not been modified since last render. 
    • if you use local state you will probably need to implement ShouldRender() as only your code knows when it needs to render due to local state changes

    blazor components have limited lifecycle events:

    • OnInitialized - called when added to the tree and initial props set. may be called twice if server pre-render done. component trees often call this mount. while it seems logical, do not do javascript interop init here
    • OnParametersSet - called after the parent set the components properties and before render. Complex logic based on parameter values or async work should be done here. if your component only uses a nested object, for performance you may need to cache and compare to prevent unnecessary renders.
    • OnRender - called during tree render. this is the inline code. supposed to be a very fast generation of html. do not make i/o calls or do complex logic here.  remember may be called more than once per virtual DOM update (as state updates are batched).
    • OnAfterRender - this is called by the virtual DOM after the true DOM has been updated. Used if javascript interop needs to access the true DOM. you can check the first parameter to initial javascript interop event hookups.
    • Dispose - the component is removed from the tree. use this unhook javascript interop event hookups

    remember and state change requires a re-render of the component tree, but only components whose state has been modified will be called. if your component is rendering too often then you probably need to implement ShouldRender(). Common if using nested properties. 

    if your component is not re-rendering when you want, then it is not subscribed  to the proper state.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, August 12, 2020 9:25 PM