After digging into the code a bit I see that invalidate() on a control will increase a counter which seems to mark the control as invalidated.
This seems to lead to a rerender.
So if you have a control that you want rerendered, is it better practice to use invalidate() or rerender()?
How does a rerender actually get triggered? (other than by explicitly invoking it of course)
Both are marked as "protected", meaning you should not call any of them unless you are really deep into developing custom controls.
A control gets invalidated when (for example) a property is changed. In that case you usually want to re-render the control, that's why setters call invalidate by default.
When you overwrite your invalidate method in your custom control, you can analyze the source of the invalidation and then decide whether you really want to rerender etc. See for example the unified.Shell which decides what to do on invalidate based on the source:
https://sapui5.netweaver.ondemand.com/sdk/resources/sap/ui/unified/Shell-dbg.js line 1539ff
Again: Protected means that it should not be called from the outside (and that it should not be necessary, except for debugging).
Is it better practice to use invalidate() or rerender()?
As an application developer
Neither invalidate() nor rerender() should be used as they're not public APIs.
As a control developer
Usually, the framework already manages rerendering of the controls automatically if one of the control settings (properties, aggregations, or associations) changes. But if it's still required to trigger the rerendering explicitly, the API invalidate() should be favored over rerender() because:
rerender()
renders the control synchronously (blocking the main/UI thread)
works only when the control has been rendered before (no initial rendering possible)
does not combine multiple state changes into a single rerendering
causes additional layout trashing
has become deprecated since 1.70.
invalidate(), on the other hand, just adds the control to a collection of to-be-rendered controls so that it can be rendered together with them in one process asynchronously (currently in the next browser task using setTimeout(fn, 0)).
I encountered such thing in my work recently. I am fixing an animation issue but the bad thing is I have to start the animation after rendering. If that is all I can handle it, but the worse thing is the control rendering twice. When the first rendering happen, the animation start to do, but the second rendering comes immediately and the DOM was restructed, then the animation is terminated and the effect is like no animation executed. After investigate the code, I found there is a place invoke render() method which will execute render immediately. After change it to invoke invalidated() method, it looks the animation works well. It looks multiple invalidate() method invocation only cause executing render once.
Related
React Lifecycle
Can someone explain the order of these operations in the react lifecycle? The picture shows events occurring both vertically and horizontally but I don't understand the order of these events. For example, "First is the mounting phase where first the constructor is called then render is called etc...". In other words, how can one understand this diagram. Is it, for each horizontal phase ie. Mounting, Updating, and Unmounting we evaluate each vertical operation ie, constructor(), render(), ...componentDidMount()?
I believe you have the right idea - for each vertical phase, you evaluate it going down. When a component mounts, it is created (via the constructor call), then it renders, then calls componentDidMount. The trick here is that the same render method is called both during mounting (after construction) and during updating (after new props, setState, or forceUpdate). That's why render is a horizontal bar - it's literally the same function that's called in both vertical flows, so representing it as disjoint processes in each vertical segment could be misleadinng. Does that make sense?
In simple term, though I am more of functional developer/hook developer than class syntasism(this is just my own made grammar😆) so my answer might not give you the best or all. To go straight to the point, below is the order of react-lifecycle(the whole life sperm of a component or UI).
render => componentWillMount => componentDidMount => componentWillUpdate =>
componentDidUpdate => componentWillUnmount
render: for class components is the first life-cycle method that ships your UI-structure to the virtual DOM when the Ui is first created(take note: when the Ui is first created it gets executed and when other life-cycles get executed, they also depend on this lifecycle because without it, your Ui in a class component won't show. This makes this lifecyle the only method that exists from first render until unmounted).
componentWillMount: hmmm, this life-cycle is been deprecated 2 years ago but to caught the long story short, you can do some synchronous event here with the mindset that the synchronous operation will be executed while the component is mounting(i call it aka alongSideLifeCycle). Mind you, does synchronous operation don't have to depend on the DOM to be set before carrying out the operation else, there will be some performance issue.
componentDidMount: hmmm, this lifecycle gets executed ones a component has been successfully mounted(but mind you, it works just the way a defer attribute works in HTML script tag,i.e its loads the operation/lifecycle to the component background and that makes the execution super fast and most times can't be noticed if you are not using some tracking devtools ). This lifecycle is the best place to carry out components-side-effects-operation aka effect(effect are things that can utter the current state, view or structure of an application e.g: async operation, DOM nodes update, DOM-API operations like timeouts, eventListeners).
componentWillUpdate: this lifecycle gets executed first anytime a component wants to update/ change anything (either it affects the UI visually or not).
componentDidUpdate: this is the best place to do some necessary clean up to avoid things like memory leak aka direct or indirect unnecessary recursion or re-occurency. e.g best place to do: removeEventLister, clearTimeout/clearInterval, any form of unsubscription.
the last one is componentWillUnMount: basically, a perfect place to the destroy the state to initialState of that component because literally, the component structure aka DOM_NODES has been technically destroyed so why keep the value-less state that its own existence depends on the existence of the destroyed component file but mind you, imperatively, that is automatically done by react-ecosystem and there is no need doing it declaratively!
Bonus point: in react, there is an algorithm called "diffing" and during componentDidUpdate, the react UI dont ship unchanges structures to the actual DOM tree again(never) but it executes the "diffing" algorithm which trys to check the current virtual DOM structure and create a new virtual DOM structure based on things that have changed, and then react do what we call "reconcialiation" which is the process of settling the Virtual-DOM changes with the real Browser DOM to know what to put into the real DOM.
This process of diffing, reconciliation and Virtual DOM made React the boss 👨🏿💻 👨🏿💻 👨🏿💻 of UI libraries because it reduces redundant rendering thereby making the ui-mounting process super fast ✈️ ✈️ ✈️
So hope that clarified you 😉
The official UI5 documentation claims:
[...] We want to make sure that the memory allocated for this helper object is freed up when the component is destroyed. Otherwise our application may cause memory leaks.
To do so, we use the exit hook. The OpenUI5 framework calls the function assigned to exit when destroying the component. We call the destroy function of HelloDialog to clean up the helper class and end its lifecycle. Nevertheless, the instance itself would still exist in the browser memory. Therefore we delete our reference to the HelloDialog instance by calling delete this._helloDialog and the garbage collection of the browser can clean up its memory.
I've set a break point on this._helloDialog.destroy(); inside of
exit() {
this._helloDialog.destroy();
delete this._helloDialog;
}
And I expect that the break point will be triggered on leaving the view, which hosts this dialog. In fact, when I switched the view nothing happened, it looks like exit() wasn't executed at all.
Besides that, I've paid attention that after opening the dialog, typing a text there, closing and then reopening the dialog, the previously typed text remains untouched.
Such behavior forces me to assume that the dialog object is never released and it might be a potential place for the memory leak.
My questions:
Why the exit hook is not triggered when I leave the view, where this dialog had been defined?
Are there any best practices / common patterns for «resetting» the dialog's content in UI5?
Why the exit hook is not triggered when I leave the view, where this dialog had been defined?
Destroying a dialog has been always a somewhat questionable practice: when the user, for example, decides to open it again; the dialog with its entire tree of child nodes needs to be recreated and rerendered on the DOM, every single control there needs to be re-registered on the Core, as well as their events on the UIArea, models need to be propagated firing all kinds of events in the meantime, memory needs to be re-allocated, etc, etc.. To sum it up - it's quite costly.
The exit-hook, which is a low-level API, is however triggered automatically when the application calls destroy() on the ManagedObject. For example, the Fiori Launchpad automatically calls currentUIComponent.destroy() when the user navigates to Home triggering the exit-hook on every child element including the dialog.
Are there any best practices / common patterns for «resetting» the dialog's content in UI5?
Yes, see my answer on How to reset reset all screen's control values.
Came across below statement in one or other form on google
Each time the underlying data changes in a React app, a new Virtual
DOM representation of the user interface is created.This is where
things get interesting. Updating the browser’s DOM is a three-step
process in React.
Whenever anything may have changed, the entire UI will be re-rendered
in a Virtual DOM representation.
The difference between the previous
Virtual DOM representation and the new one will be calculated.
The
real DOM will be updated with what has actually changed. This is very
much like applying a patch.
I am new to React and would like to understand above how above three points used to to work in pre-React Era say in jQuery (or native JS).
jQuery
HTML was constructed on server side, sent back to browser. Browser will parse, render, layout and paint it.
Say any new DOM element is created or hidden either on some user event or on load.
Will jQuery recreate the complete DOM? From third point stated above looks like React just update the DOM for only the part that has been changed but other system (primarily jQuery or native JS) will recreate the complete DOM. Is that correct?
Is the third point true only for DOM changes or even when state of any UI component changes like filling the text box/dropdown etc?
Will jquery recreate the complete DOM ? From third point stated above looks like React just update the DOM for only the part that has been changed but other system(primarily jquery or native js) will recreate the complete DOM. Is that correct?
jQuery
No, jQuery doesn't recreate the DOM. Instead it navigates the DOM tree with selectors provided to make the appropriate changes. This makes jQuery very similar to React, but its performance issues come with the way the code was designed and it's heavy usage of the facade design pattern. This is normal for any larger library that needs to support multiple browsers like Chrome, Firefox, Opera, etc.
Angular 1
A framework that used to repaint the entire DOM was Angular 1. The client would make some changes, and rerender whenever $scope.apply or $scope.digest was called. This, combined with a huge number of listeners on large pages for two way data-binding was one of the big reasons why Angular had to undergo significant changes to stay competitive. Angular 8 is probably equivalent to React, but one has seen more adoption then the other.
React
React only updates the DOM that was changed. This is part of its "secret sauce". Along with its component centric architecture and early adoption of one way data-binding, it's seen a lot of success. Arguably React is starting to get more bloated since there's such wide adoption. This is normal for any project that gets mainstream usage. It's only a matter of time until people view React as a ton of performance problems and we'll create a new framework.
Alternatives
There are even faster frameworks than React, such as Elm lang. Nothing ever beats pure Javascript (e.g. document.querySelector()) since at their core, all frameworks use it. At that point you start hitting other trade offs such as lack of external libraries you can depend on, or difficulty in maintaining large front end codebases.
Is the third point true only for dom changes or even when state of any UI component changes like filling the text box/drop down etc ?
For jQuery or pure JS the third point is not true. There's a custom on-click handler of some sort that will run a function that makes a small change.
For something like Angular, that could be true if there are changes to scope that trigger a repaint. The same applies for React. If your submit button is supposed to redirect you to a completely different page, it will basically be repainting the DOM.
I was working on a component that has a button which toggles a boolean. This boolean is supposed to determine if a child component in the HTML need to re-render or not, since I want the ngOnInit function in the child to be re-run.
The situation is described in the app component here: https://codesandbox.io/s/angular-qxtm8
The app.component is the parent and second.component is the child.
I have tried three different solutions. They are onTestClickOne, onTestClickTwo, and onTestClickThree in app.component.ts. onTestClickOne and onTestClickTwo successfully re-triggers the ngOnInit in the child component. We can see the console log in it is printed on the console whenever I click the corresponding buttons. However, onTestClickThree didn't work.
I'm not 100% sure why onTestClickThree didn't work, and onTestClickTwo did.
My guesses are the following:
onTestClickTwo works because the change detection in Angular is run after the event handler has been executed. So, it will detect the boolean has been set to true. After that, the event loop will get the callback of the setTimeout and put it into the stack. Angular will execute change detection after finishing the callback.
onTestClickThree didn't work because, by the time Angular runs change detection, the boolean is already true. Angular doesn't know that it has been changed.
Let's tackle the main issue there, which is your design : why would you re-render the component to trigger ngOnInit again ?
Sure, in the case of your example, that's no big deal. But what happens for a fully coded component, making http calls, having children and all ? That will cause some severe performance issues.
Instead of re-rendering the component, you should use a function to do that.
If the event (that is initially supposed to re-render the component) comes from the child, then use an #Output. If it comes from the parent, use a #ViewChild reference.
As you can see it works well, without any detection issue.
See the following Stackblitz: https://stackblitz.com/edit/angular-psqzbo?file=src%2Fapp%2Fhello.component.ts
Notice that there are two bindings to the width member: one is in the template, and the other is a host binding. The host binding is commented out right now. Notice that no ExpressionChanged error is being thrown-- this is because this.cdr.detectChanges() is being called after we update width in ngAfterViewInit.
Now uncomment the host binding. Observe that an ExpressionChanged error is thrown. Why? What makes these bindings different? Is this a bug?
EDIT: This is not a dupe of the linked question. I know why detectChanges is needed here, my question is why it is not working on the host binding. Please re-read.
This error occurs for you because you are making a change that invalidates the previously rendered component view.
Quote from this documentation
Angular's unidirectional data flow rule forbids updates to the view after it has been composed. Both of these hooks fire after the component's view has been composed.
What this means for your situation:
When Angular first renders/composes the view of this component (prior ngAfterViewInit) the width of the element is set to an initial value. This code changes the width of an element, which causes a change to the view.
I think this example using background color makes this more obvious. In the first pass rendering the view, the color is red (and you can briefly see this on the screen). In your situation, the width binding is undefined on the first pass through.
Then, the ngAfterViewInit causes a change that makes the previously created view invalid, changing the width or the color, triggering the error. I translate this error as Angular saying "I did a bunch of work and made a view that was perfect, then you did something that made that work worthless. You shouldn't do that, because it interrupts some performance optimizations/assumptions I have".
This can be fixed by ensuring the component change happens after the ngAfterViewInit method has finished running, by using setTimeout. Fixed example Or by ensuring the component change happens before the view is rendered by moving it to ngOnInit.
You may notice that I do not have detectChanges in the examples I created. This is intentional, as that is a red herring and actually has no relation to the problem (though you correctly state this is necessary for the template binding to work). When the host binding is commented out in your example, there is no expression problem because this.width has no impact on the rendered view of the component.
There is no issue when the binding is in the template because this is causing changes to the content of the component - not the component's own view.
Speculation / Things I don't fully understand:
I believe this behavior boils down to changes to the Shadow DOM vs Light DOM (I'm making guesses based on info from this SO question). At the time ngAfterViewInit is running, I believe there is already an empty tag <my-component></my-component> in the Light DOM (IE actually on the page). Changing the child content does not cause an issue, because at this point it doesn't actually exist on the page and is part of the Shadow DOM. However, changing a host binding triggers a change to the real elements on the page (part of the Light DOM) - hence the error.