My Vue.js app is currently slow and I have a feeling that there might be too many unnecessary observers.
In my app, I display a tree made of a root node who has children and whose children have children and so on. It looks like this :
{
label: "root node",
// ... more props
children : [
{
label:"child A",
// ... more props
children:[
{
label:"deep child A1"
// ... more props
},
// ... more children
]
},
// ... more children
]
}
There is also a component called "Node" with a prop called "node" where I pass the node as data, each "Node" component has the responsibility of displaying its dynamic props and immediate children and it all starts with the root node.
The problem would be, from my understanding of Vue.js, that the observers are recursive for the children and grand children at each "Node" component, making a lot of redundant observers at depths that dont have an impact to the grand parent nodes.
So my question is, what are the strategies or common patterns to avoid unnecessary observers in case of a recursive data structure like this ?
Related
I'm wondering what's the best practice to do two separate fetches to data that would belong to the same Model. One to get all Users data and a separate one that would request their Traits and add them to each User.
I think I could create a reference in User, to fill the data, but im not sure how to create the custom reference since it should be an array.
export const User = types
.model('User', {
id: types.identifierNumber,
...
traits: types.maybeNull(TraitsbyUserReference),
})
const TraitsbyUserReference = types.maybe(
types.reference(Trait, {
get(identifier: string, parent): {
return (parent as Instance<typeof TraitsStore>).getAllTraits()
},
set(value) {
return value; // this is what doesnt work out because i'm fetching a whole array
},
}),
)
Also, is this a good practice or are there other better ways of getting this result?
Thanks!
In terms of defining the model, you might try switching from maybeNull to an optional array with a default value in your model -
...
traits: types.optional(types.array(Trait), []),
...
As such, the model will always be instantiated with an empty traits collection.
In terms of the TraitsbyUserReference, I am not following what abstraction that you need with the dynamic store look-up. You could create an action (e.g. User.actions(self => ...)) to look-up the traits as a separate api -
getUserTraits(){
/* this will vary based on your implementation of TraitsStore and how it is injected */
const traits = self.TraitsStore.getAllTraits(self.id);
self.traits = traits;
}
So how can I have a component that take's a few default props from a parent, but also has a store? These seems seem to conflict.
<Mycomponent foo="bar">
...this means my component has some state/prop called "foo", with a value that my store does not know about. Doesn't this break the pattern's visibility design? Do I need to pass these initial props down into my store in one of the react life-cycle methods? That seems like extra work and also fragile.
I'm also finding it a little frustrating to map my store's state into the component props object. For example I have a form with some fields and they can take defaults from a parent
<MyForm input1="red", input2="blue">
The store's initial state might look like this
{
status: valid,
modified: false,
form: {
input1: ""
input2: ""
}
}
And my local props before the component is rendered looks like this
{
input1: "red"
input2: "blue"
}
But when things get remapped in mapStateToProps...
var mapStateToProps = function(state) {
return state;
}
it ends up looking like this.
{
form: {
input1: "red"
input2: "blue"
}
status: valid,
modified: false,
input1: "red"
input2: "blue"
}
Because the key1 and key2 props outside the "form" object are from the initial component properties passed down from the parent. They are not part of the store's state object. I really want the props object to match the store, but it seems like it can't unless I start deleting stuff. It seems we are using the this.props object in two ways and it feels incorrect.
What I really want is the properties from the parent to go directly into my store and from there I want my component to render based on my store only. I feel like react-redux does not fit will into the react life-cycle, or at least I don't understand how its supposed to be used.
Checkout documentation: https://github.com/reactjs/react-redux/blob/master/docs/api.md - mapStateToProps - parameter: ownProps:
If ownProps is specified as a second argument, its value will be the props passed to your component
You have examples below API description. It might be helpful.
Moreover, I think that you make something wrong. Redux works well with internal sate of components. I use state to manipulate data, that reflects on GUI but they are irrelevant to the rest of the application (example: isVisible flag in accordion menu).
You should use containers (components, that are connected to the store) only if you have obtain some data from store, otherwise use dumb components.
Compose containers, nest them if needed, avoid retrieving big chunk of state in one container.
You should also take data, that is important for your component. If your container has to know all about entire state...well, it seems that something is wrong with the architecture of the application. I map only few properties from state directly to the props:
const mapStateToProps = (state) => {
return {
status: state.status,
field1: state.form.field1,
field2: state.form.field2
}
}
So I end up with flat map.
Notice, that redux performs shallow comparison of new props:
https://github.com/reactjs/react-redux/blob/master/docs/api.md
If true, implements shouldComponentUpdate and shallowly compares the result of mergeProps, preventing unnecessary updates, assuming that the component is a “pure” component and does not rely on any input or state other than its props and the selected Redux store’s state. Defaults to true.
And don't use redux in simple apps, it is for bigger projects.
Question. How do I avoid n+1 queries with Spring Data REST?
Background. When querying Spring Data REST for a list of resources, each of the resulting top-level resources has links to the associated resources, as opposed to having the associated resources embedded directly in the top-level resources. For example, if I query for a list of data centers, the associated regions appear as links, like this:
{
"links" : [ {
"rel" : "self",
"href" : "http://localhost:2112/api/datacenters/1"
}, {
"rel" : "datacenters.DataCenter.region",
"href" : "http://localhost:2112/api/datacenters/1/region"
} ],
"name" : "US East 1a",
"key" : "amazon-us-east-1a"
}
It is pretty typical, however, to want to get the associated information without having to do n+1 queries. To stick with the example above, I might want to display a list of data centers and their associated regions in a UI.
What I've tried. I created a custom query on my RegionRepository to get all the regions for a given set of data center keys:
#RestResource(path = "find-by-data-center-key-in")
Page<Region> findByDataCentersKeyIn(
#Param("key") Collection<String> keys,
Pageable pageable);
Unfortunately the links this query generates don't overlap with the links that the data center query above generates. Here are the links I get for the custom query:
http://localhost:2112/api/regions/search/find-by-data-center-key-in?key=amazon-us-east-1a&key=amazon-us-east-1b
{
"links" : [ ],
"content" : [ {
"links" : [ {
"rel" : "self",
"href" : "http://localhost:2112/api/regions/1"
}, {
"rel" : "regions.Region.datacenters",
"href" : "http://localhost:2112/api/regions/1/datacenters"
}, {
"rel" : "regions.Region.infrastructureprovider",
"href" : "http://localhost:2112/api/regions/1/infrastructureprovider"
} ],
"name" : "US East (N. Virginia)",
"key" : "amazon-us-east-1"
}, {
"links" : [ {
"rel" : "self",
"href" : "http://localhost:2112/api/regions/1"
}, {
"rel" : "regions.Region.datacenters",
"href" : "http://localhost:2112/api/regions/1/datacenters"
}, {
"rel" : "regions.Region.infrastructureprovider",
"href" : "http://localhost:2112/api/regions/1/infrastructureprovider"
} ],
"name" : "US East (N. Virginia)",
"key" : "amazon-us-east-1"
} ],
"page" : {
"size" : 20,
"totalElements" : 2,
"totalPages" : 1,
"number" : 1
}
}
The challenge seems to be that the data center query returns links that aren't particularly informative once you already understand the shape of the data. For example, I already know that the region for data center 1 is at /datacenters/1/region, so if I want actual information about which specific region is involved, I have to follow the link to get it. In particular I have to follow the link to get the canonical URI that shows up in the bulk queries that would allow me to avoid n+1 queries.
The reason Spring Data REST works like this is the following: by default, we assume every application repository a primary resource of the REST service. Thus, if you expose a repository for an entity's related object you get links rendered to it and we expose the assignment of one entity to another via a nested resource (e.g. foo/{id}/bar).
To prevent this, annotate the related repository interface with #RestResource(exported = false) which prevents the entities managed by this repository from becoming top level resources.
The more general approach to this is starting with Spring Data REST letting you expose the resources you want to get managed and default rules applied. You can then customize the rendering and links by implementing ResourceProcessor<T> and registering your implementation as Spring bean. The ResourceProcessor will then allow you to customize the data rendered, links added to the representation etc.
For everything else, manually implement controllers (potentially blending into the URI space of the default controllers) and add links to those through ResourceProcessor implementations. An example for this can be seen in the Spring RESTBucks sample. The sample project uses Spring Data REST to manage Order instances and implements a custom controller to implement the more complex payment process. Beyond that it adds a link to the Order resource to point to the manually implemented code.
Spring Data REST will only create the representation you describe if the serializer that is configured inside the Jackson ObjectMapper is triggered by seeing a PersistentEntityResource, which is a special kind of Resource that is used inside Spring Data REST.
If you create a ResourceProcessor<Resource<MyPojo>> and return a new Resource<MyPojo>(origResource.getContent(), origResource.getLinks()), then the default Spring Data REST serialization machinery will not be triggered and Jackson's normal serialization rules will apply.
Note, however, that the reason Spring Data REST does associations the way it does is because it's very difficult to arbitrarily stop traversing an object graph when serializing to JSON. By handling associations the way it does, it guarantees that the serializer won't start traversing an object graph that is N levels deep and become much slower in performance and in the performance of the representation going over-the-wire.
Ensuring that Jackson does not try to serialize a PersistentEntityResource, which is what it's doing in the default configuration, will ensure that none of the Spring Data REST handling of associations is triggered. The down side to this, of course, is that none of Spring Data REST's helpers will be triggered. If you still want links to the associated resources, you'll have to make sure you create those yourself and add them to the outgoing plain Resource.
Please can you help me with the problem below:
I have 2 domain classes(Parent, Child) which I do not want to be mapped to a table, so I put mapWith=none. However, when I do parent.validate() I want the validation to be cascaded to the child. How can I enable cascade validation for domain objects which are not mapped to a table?
Many thanks in advance!
Don't know if you can by design.
You could optionally add a customValidation() method on the parent object to initiate a check which is looping over the children and invoking their validate() methods. Any errors on the children could then be added to the errors object of the parent object.
boolean cascadedValidation() {
this.validate();
children.each {
if (!it.validate()) {
it.errors.allErrors.each { err ->
// Bind somewhere on parent object
}
}
}
return this.hasErrors();
}
I have this scenario to implement: a collection of parentEntities, each parentEntity having zero to childEntities. I need to implement a multiple-delete form (checked parentEntities will be deleted), with this rule: if a parentEntity has children, it can be deleted only after all its children have been linked to other parentEntities.
So I have the Index form for the parents, and on submit I post to the "Delete" action. For each checked parent, if it has zero children I delete it, else I redirect to children edit view. When there is no more child linked to the initial parent, I must return to the parents "Delete" action and continue from where I left (delete the current parent whose children I just re-affected, then delete the next checked parent).
My problem is: HOW do I write the return from children edit to the parents delete?
I use:
return new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Parents", action = "Index", page, IDsToDelete = idCollection }));
but I get the error:
http://localhost:64209/Parents/Delete?page=0; The resource cannot be found.
Thank you for helping.
Manu
Ok, it's solved - I just needed to pass the string of checked items pending for delete.