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)
Related
so I was programming an app with React and what happened is that I have a component with a fairly large list in its state. I am fetching a JSON file from network and then storing a filtered copy directly to component's state. Might be unoptimal solution BUT I think that's still okay and React should handle it I mean, it's just 10 kB.
Anyway I decided to add a search input to my component and store its value to its state. Now I have both the large list and searchInput in its state which I am setStating every onChange and filtering the list based on that.
And that is super slow. Every setState is refreshing the list and doing componentUpdates on every children and subchildren of the component which basically makes the search unusable.
So my question is how to fix this issue? Should I store the filtered list in the redux store instead of local component state? That doesn't seem that good a solution either as I have now a global searchInput value which I have to reset and delete on leave and which I think is better as local value.
Here's how it's currently:
list -> component -> filter list -> child -> split the list into 4 -> subchild -> map the sublist -> render the list item values
What I thought too was adding additional list with values showing which items should be hidden/shown so instead of manipulating the large list I am manipulating smaller list of item ids. Still that seems a bit silly, this thing shouldn't be this hard I mean people have been doing lists with JS and HTML quite a while now. I was thinking about re-creating the same component with Vue just to see would it be better (which I think it would).
I see your problem. It's not actually the setState that is slow but actually the rendering and the way you search things in said state.
If I were you I would invest time in 2 things:
debounce for the searching
debounce doesn't trigger the search immediately but "waits" a set amount of time for the user to stop typing and then it triggers the function.
Here's an example in React:
// you can use another one. I've just used this one before and it works
import debounce from "throttle-debounce";
class SearchBox extends React.Component {
constructor(props) {
super(props);
// "waits" for 750 ms
this.search = debounce(this.search, 750);
}
search() { ... }
render() {
<input type="text" onKeyUp={this.search} />
}
}
If you have a big list then memoization is a good bet. You can use react-virtualized for that.
React components for efficiently rendering large lists and tabular
data
You can even access the List demo here
A good UI design and pagination
react-virtualized List component will only render what is being seen by the user. So if you have a nice UI design you can juice up a lot of performance from a really big list of values.
Many times it comes down to how you display data to your end-users. So you can add pagination to your data and fetch more either with pagination links or a infinite scroll feature.
Hope I helped
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?
I'm writing a series of components in react that will make a structure like this:
Field
|_BasicField: plain input field.
|_ListField: a list of Fields.
|_Entity: a complex Field that can have BasicFields, ListFields
and Entities as its children components.
The code for this structure you can find it here in my repo.
The data used to generate the scraping form is in this file.
What happens is that once the first level of the form is rendered, children Fields are dynamically generated, but they don't seem to be rerendering as expected. I'm just trying to make sense out of it, and it seems to me that react had the children components cached in a previous state, but when the state updates it doesn't update the children components, but renders them as they where in the previous state, or something like that, I'm really lost here. I don't even know where to look for the error. Please any help will be really appreciated! Thanks.
The ReactJS website states that,
When the data changes, React conceptually hits the "refresh" button, and knows to only update the changed parts.
What does this mean exactly?
How is this achieved?
How is this different from what AngularJS does?
Reactjs create a virtual DOM and does not touch to the real DOM. Then when some of DOM changed, it will use diff algorithm to find out what is different from previous state of DOM elements, finally just replace that changes. You can see this link for that algorithm to have more understanding. And you have to know what is shadow DOM as well.
Here's a great resource explaining why ReactJS was developed and how it differs from other frameworks like AngularJS:
http://www.quora.com/Pete-Hunt/Posts/Facebooks-React-vs-AngularJS-A-Closer-Look
"A lot of the heavyweight contenders for MVVM frameworks have a hard time rendering large amounts of data, like in lists and such. React doesn’t have that problem, as it renders only what’s changed.
For example, if a user is viewing a list of 100 items rendered with React, and he or she changes the third one down somehow, only that item gets rerendered, leaving the other 99 items unchanged."
(https://www.codementor.io/reactjs/tutorial/react-vs-angularjs)
I need to use two states in parallel, one for my page and an other for a modal with several sub states.
Right now calling the modal state will wipe out my page since the page state changed.
Create a child state child of my page wouldn't be a solution since the modal will be used on several pages.
Example:
$stateProvider
.state('user', {}) // page
.state('bookshelf', {}) // page
.state('books', {}) // modal
.state('books.read', {}) // sub state of modal
So if I'm on user and open my modal then the state would change to books, my modal would then have the content but the page content will be wiped out.
How do I fix it?
I believe the way you're looking to do this is not possible with UI.Router currently. What you're describing is a modal component (which would ideally be written as a directive), which tracks it's state independently from the main state.
The way to think about it, is that UI.Router works by creating a state tree. At any given time you can only be looking at one branch of the tree. You can go deeper down a branch (ie: book, book.open, book.open.checked), but you can't be in two places at once.
Another issue with the problem above is how do you serialize the state of the two different trees into one url? It's not to say it can't be done, it's just a hard problem to solve.
Checkout these issues:
https://github.com/angular-ui/ui-router/issues/119
https://github.com/angular-ui/ui-router/issues/384
https://github.com/angular-ui/ui-router/issues/475
Also checkout these repos, they might be further along the lines of solving the problem.
https://github.com/afterglowtech/angular-detour
https://github.com/stu-salsbury/angular-couch-potato
As far as solving your immediate problem, I think the 'easiest' way would be to ditch controlling the state of the modal inside your state config.
Instead, I would add some sort of root or abstract state, and then track whether the modal is open there. Then, you can communicate between controllers using events as shown here. Note: There are performance implications with listening to $rootScope, so be sure to research those. However (someone feel free to correct me), the implementation here doesn't have those problems, because the AppCtrl is never destroyed.
Jan 15, 2015 Edit
Turns out this is a pretty popular use case, and one of the core contributors to UI Router maintains a plugin/addition called UI Router Extras
It also includes utilities for lazy loading, called "Future States" which are very helpful.
That being said, one feature I'm hoping to get time to work on is maintaining all state within the URL (or perhaps, local storage) and allowing for reusable state "components". The latter is in the UI Router roadmap as well.