I've wrote a plugin for Vue and have some issues on rerendering. In fact there is a new filter, which translate a given text by the global defined language. When the language changes, the text should be translated again. Cause there is no event queue for filters here, I want to force all components to rerender themselves, if the language changes. So the filter function would be evaluated new.
I know that I can rerender a component itself with vm.$forceUpdate(). But how can I told the whole Vue component tree to rerender? Cause this only happens, if the user change the language, the performance issues should not be a problem, cause at least the user do not have to reload the whole page and can do this offline.
Any suggestions?
You can use a key attribute in the component root. Change it and the component tree will be rerendered.
Per docs (bold is mine):
It can also be used to force replacement of an element/component
instead of reusing it. This can be useful when you want to:
Properly trigger lifecycle hooks of a component
Trigger transitions
For example:
<transition>
<span :key="text">{{ text }}</span>
</transition>
When text changes, the <span> will always be replaced instead of
patched, so a transition will be triggered.
Related
I've got a complex component which does all its rendering in a render function. There are multiple parts to this, and different bits of the view get rendered - one of these things is a filter bar, showing the filters that have been applied.
What I'm noticing happening, is if I apply a filter which in turn presents this bar, it causes everything else to be fully re-rendered. This is causing a number of other issues and I need to try and stop it from happening.
I've never come across this issue when using normal templates as Vue seems to handle these very intelligently, but I have no idea how to tackle this. The only thing I can think of is setting a key on each thing I don't want re-rendered but not sure if this will a) solve the problem, and b) be possible for the content that is passed in through a slot
Has anyone else faced this issue, and if so how can it be solved?
I had a similar issue when using vuetify text inputs in a complex component which was causing the app to slow down drastically.
In my search I found this link which was specific to vuetify:
high performance impact when using a lot of v-text-field
then found out that this is actually a vue thing from this GitHub issue:
Component with slot re-renders even if the slot or component data has not changed
and there is plan to improve this in it is tracked here (vue 3 should resolve this issue):
Update slot content without re-rendering rest of component
so after reading through all these I found some workarounds that helped me a lot to boost the performance of my app, I hope these will help you as well:
divide that complex component into smaller ones specially when there is some bit of code that changes data that bounds to template causing re-rendering (put them in their own component)
I moved all data layer control to the vuex store, instead of using v-model every where and passing data as events and props, all the data is updating in the store through an action and read from the store through a getter. (from data I mean somethings that is being looped in the template in a v-for, API results, and so on... all of them is being set, updated and read through the store. my components still have the data object but only for the things related to the style and template control like a boolean to control a modal or an imported icon which is used in the template and alikes)
lastly I wrote a function called lazyCaller which its job is to update the values in the store with a delay (when immediate data update isn't necessary) to avoid rapid updates comping from something like a text input (with out this every key stroke trigger the value update action)
This always puzzled me. If I said "Thank you React, your state hooks are awesome, but I'm just gonna do my direct dom manipulation here", would react still do the virtual dom comparison in order to update only that specific item? would I still benefit from the virtual dom 'situation'?
Is there any difference between using react to directly manipulate dom without states, and using a standard HTML file with imported vanilla js code?
For clarity, here's an example,
Let's say I have function printHellol() triggered by a button 'click' in my JSX. The function targets the ID of an element and changes the text content.
I couldn't find the answer anywhere. Thanks!
Anything that his held in state becomes part of an object that react renders as a detached element from the dom. It's in essence creating a separate environment for all states. Anything that is used outside of state can be considered to be part of the direct dom object, causing the page to be rerendered when updates occur. In other words you would need to use the react specific state if you want to access the virtual dom specific environment.
In our experience, thinking about how the UI should look at any given moment, rather than how to change it over time, eliminates a whole class of bugs.
From React Docs
From my understanding, this means that React only updates what's necessary, rather than destroying and re-constructing the entire DOM tree again. Am I wrong?
Can anyone please help me understand the quoted statement?
Thanks.
From my understanding, this means that React only updates what's necessary, rather than destroying and re-constructing the entire DOM tree again. Am I wrong?
If you want to know the short answer, I have to say it is true, React will update the necessary elements in DOM whenever it needed.
But if you want to know how it's done, and when React will update the DOM and its element I have to it is varying to different several things, like project architecture, using proper methods (proper hooks in functional component eg. useCallback, useMemo, and so on) and so on.
When it truly gets rerender then?
As far as I know, there are two ways React finds out when to rerender the DOM.
Passing elements to ReactDOM.render
Update a state
What is ReactDOM.render?
This will call the render() method from react-dom (Where we usually import it as ReactDOM) and it will render a React element into the DOM in the supplied container and return a reference to the component (or returns null for stateless components). Also if the React element was previously rendered into the container, this will perform an update on it and only mutate the DOM as necessary to reflect the latest React element.
What does state mean?
The state object is where you store property values that belong to the component. So when you got a component and that component has their own specific variables where changing them should affect the DOM you should use it, then whenever state gets changes the component will be updated.
So what I even talk about project architecture and this stuff, when I didn't mention anything about it above?
Let's say we got a project with one parent component and 3 child component just like this:
Component 1
- |- Component 2
- - |- Component 3
- - - |- Component 4
So whenever you a state in Component 4 all of the DOM elements will be get rerendered, why then? Because Component 4 is a child of Component 3 and so on, so when the child state gets change it will force the parent to rerender then the whole DOM will rerender once per state change.
Final Note
So, at last, we should be always considered a good architecture and hierarchy for our project or when it necessarily use built-in methods like useMemo to avoid such a thing.
I have a large array that I am using in a component (component A) with *ngFor with a nested *ngFor.
Component B initialises a jquery plugin which registers a document mousemove event handler, I am using this.zone.runOutsideAngular to init the plugin and I am calling this.ref.detectChanges() in the callback as I need to update the UI on mousemove inside the component B.
Component A is not a child of component B.
As soon as the component A is rendered change detection becomes very slow. the array does not change and I am using the ChangeDetectionStrategy.OnPush strategy for component A but when I fire ref.detectChanges() inside component B, ngDoCheck gets called on component A and I can see a noticeable jank on mousemove.
Is there a way to tell angular to completely ignore the large array of items in component A and allow me to handle when the UI should be updated? I thought that using ChangeDetectionStrategy.OnPush would give me what I need but I have tried removing all #Input()s from component A and anytime I call this.ref.detectChanges() inside component B it is still firing ngDoCheck and it is obvious that this is very slow.
I can scroll through the list of items no issue, but it is when I am triggering the detectChanges inside the mousemove on component B that is causing the issue. I know I could manually update the DOM but I think this would just be a workaround as it would only address the jank on mousemove and not the issue around the change detection being slow.
I have got to the bottom of this issue.
The problem was that inside component A for the nested *ngFor I was using a child component to render each sub item which meant that although I was using the ChangeDetectionStrategy.OnPush strategy, it still required a ref check for each item.
I have now moved the html from the child component into component A directly and this has had a huge impact on performance.
this.ref.detach() to remove the detector from from the tree completely, that should stop the checking. Then you can still call detectChanges to do it manually, and reattach to bring it back online.
Maybe also debouncing the mousemoves (rxjs debounceTime()) might help, unless you really need to track every mousemove?
One more optimization if you already didn't, add trackBy: yourTrackByFn to the ngFor(s).
This is my component architecture (relevant component in bold):
App
Attributes
Evaluator
Commander
StyleGrid
DataGrid
This is part of my merged reducers:
Root
appState
isImageFocused (boolean)
...
When isImageFocused changes, the color of the following image changes:
The image is part of the Evaluator component. Everything works as expected, nevertheless, I've noticed the entire Attributes's component-subtree gets re-evaluated (shadow DOM comparison, right?) for that small change. As far as I understand, this means Attributes, Evaluator, StyleGrid and DataGrid components are all re-evaluated (even if not re-painted).
Is there a way to avoid those re-evaluations on small changes?
I am asking because I saw a performance hit when I ran the app with mrdoob's stats.js tool.
I thought of using 'shouldComponentUpdate' but that means every time I add a sibling for Evaluator I need to tell it not to update when isImageFocused is updated.
I wish there was something such as "silentProps" which would enable me to pass props through the component tree to end leafs without causing a massive re-evaluations. Is there?