I am using react to build my own web app, but I am wondering how does the following assumption make react diff faster. In other words, what does react base this assumption on?
Two elements of different types will produce different trees.
That assumption enables React to find out minimum number of operations to transform one DOM tree into another one in O(n) time.
However, the state of the art algorithms have a complexity in the order of O(n3) where n is the number of elements in the tree. If we used this in React, displaying 1000 elements would require in the order of one billion comparisons. This is far too expensive.
By assuming that elements of different types have different trees, react will save valuable computation time by avoiding too many comparisons to convert one tree into another. So without this if you have the following structure
<div>
<span> </span>
<span> </span>
</div>
and if your new structure is
<span>
<span> </span>
<div> </div>
</span>
React will now have to compare whether the first span child was there or not. Then it will have to compare whether second child div is there or not; now it finds that it wasn't there so it will have to only change the second div along with the outer parent tag. (Imagine doing this for 1000 elements)
They have also mentioned
In practice, these assumptions are valid for almost all practical use cases.
And from my experience in almost all cases it is always true.
React creates and keeps an exactly same copy of actual DOM which is termed as virtual DOM. This will have everything the actual DOM has like objects and their types and their values. Whenever there is any change in the actual DOM, react takes a snapshot of it right before the update and compares it with the virtual DOM, and based on the differences found it goes and updates the actual DOM.
So if you have two elements of different types, there will be a different tree.
This is my understanding, let me know if you find something else.
Diffing Algorithm Assumptions
If two react elements have a different type, the algorithm assumes their content to be different.
Note: If two react elements have the same content but different types, the diffing algorithm takes the content differently and unnecessary modification will be done in the real DOM.
Try out this by creating two-component, render one of them in the first render and render the other in the future render. If you inspect the element in the developer tool you will see unnecessary modification is made in the real DOM.
https://codesandbox.io/s/diffing-algorithm-assumptions-7gsxxw
We are providing a key as a prop to every element in case we are rendering a list of items.
Related
That's obvious that for the diffing algorithm in React keys are essential. But I was wandering, why React just can't automatically generate keys based on the content we iterate over?
I also assume that items can share some similarity, or cab be identical in terms of content, but isn't it possible to generate keys once user open a page and somehow attach them to the items, so it is stable?
Or maybe there where attempts to solve the problem, if so, I would be grateful if you share it to me.
Update
Thank you guys for your answers, I've learnt a lot!
Also a thing I had in mind: what we developers do when there is no stable id (e.g. user added an item which is not yet saved into DB). In the cases we just generate id, and attach it to the object, or element in an array, but we do not generate ids on a fly, so it remains stable over time.
What if React just generate ids for all arrays which are involved into rendering process, in other words, arrays which are directly used in render function?
It can be done only once, during phase Commit phase, or whatever. Also I believe, the id can be readonly, or something, so user can't erase the id.
p.s.s
While I was writing p.s. question above, I realized, autogenerating id for arrays wouldn't work, since I've missed two things. All side effect react can do only during the Commit phase, but not Render phase. But that's not the main problem.
The main problem is when we use filtering or sorting on a back-end side. Since we receive a new array, filtered one, we would need to regenerate ids for those elements, but basically, that's the same html elements, in which we can change content to match filtering order. That's the same as Slava Knyazev mentioned.
React can't generate keys, because the entire point of keys is for you to help React track elements during it's tree-diffing stage.
For example, lets say you have the following code, where you naively use content instead of identifiers for your keys:
const people = usePeople(); // [{ id: "1", name: "James"}, {id: "2", name: "William"}]
return <ul>{people.map(p => <li key={p.name}>{p.name}</li>}</ul>
The above code will function and behave as you would expect. But what happens if the name of a person changes? To understand it, lets look at the tree it generates:
ul
li(James) James
li(William) William
If James becomes Josh between renders, the new tree will look like this:
ul
li(Josh) Josh
li(William) William
React will compare the two results and conclude the following:
li(James) is to be removed
li(Josh) is to be added
However, if we set our key prop to p.id, then the old and new tree will look as follows, respectively:
ul
li(1) James
li(2) William
ul
li(1) Josh
li(2) William
And when React compares the two, it will identify that James has become Josh, and needs only the text adjusted.
In the first scenario, the <li> component is completely destroyed, and a completely new component takes its place. Both of these actions run a complete React lifecycle for the component. In the second, it remains untouched, and only the text inside changes.
While in this contrived scenario, the performance penalty in the first case in minimal, it may be very significant with complex components.
I believe, unless your data is 100% certainly going to sort in one way and never change, key={index} isn't a good key (which is what I assume you want your auto-generated keys to be). You'd ideally want something that is unique to each item, regardless of the order.
It's explained in more detail in the new beta react docs https://beta.reactjs.org/learn/rendering-lists#where-to-get-your-key
I think what you are implying is React could potentially choose to use something like a stable hash (say sha1 on a serialised string or something) on the object to generate a unique key. I think this actually would work in many cases, and even gave me pause for thought for a while! Your question is actually a really good one, and a deep one.
However, it wouldn't work in every case. I think it would work only on a static object which has no methods or anything attached. On a JS object, not all properties are enumerable. Hashing something could only ever happen on the enumerable objects of properties, but the dev may have non-enumerable yet-still-unique methods attached to these objects. In fact, even enumerable methods cant really be serialised reliably and they could be what makes the object unique. Not to mention the complexities of reliably hashing something with prototypical inheritance involved.
I suspect there's also a performance aspect to this. Hashing is cheap, but no that cheap. Most cases can be keyed by just referencing a unique ID in the object, which is vastly cheaper. When enumerating a very large number of objects, these things matter, and so its better to defer to the developer. After all, if you really do want to hash it, its just one function call in userland -- and this saves great confusion on developer side when it doesn't work. The Principle of least astonishment comes to mind.
There's also an aspect of how this would limit the power of how expressive JSX can be due to it basically allowing free-form JS. You would probably have to supply some low level <React.Map> component primitives in order for react to supply this default key handling which implies you are a bit more restrained on what you can and can't do (complex functional chains).
I dont understand why i should you key property.
{data.map((dataInfo, index) => (
<div key={index}>
<h1>{dataInfo.chapter}</h1>
<LessonGrid data={dataInfo} />
</div>
))}
First of all, you should know that there is an internal DOM (Virtual-DOM) which react maintains. Then once you make some changes react won't update the real dom immediately, the react-DOM will compare the current result with the previous result (which is known as diffing) and then only pass the changes to the real DOM. Virtual DOM is a virtual representation of the real DOM.
Yes, you should always use key every time you use a map,
So let's say you have an array of div's that you want to render
<div>Sam</div>
<div>Mike</div>
Now let's say you want to add a name at the last, then your dom structure will look something like this
<div>Sam</div>
<div>Mike</div>
<div>Jason</div>
Now Virtual-DOM will compare the current result with the previous result and will figure out a div has been added to the last, so it will push that div to the real-DOM and changes will be reflected
Now let's say you want to add a div in the starting rather than at the last like this,
<div>Jason</div>
<div>Sam</div>
<div>Mike</div>
Now Virtual-DOM will again compare these changes line by line like this,
//Previous result //Current result
<div>Sam</div> <-Changed-> <div>Jason</div>
<div>Mike</div> <-Changed-> <div>Sam</div>
<-Added-> <div>Mike</div>
So now Virtual-DOM will compare the results and it will figure out that each div has changed, So it will push the whole array to the real-DOM instead of only one div that has been pushed to the top.
Note
This is where the key comes into the picture.
//Previous result //Current result
<div key={1ab}>Sam</div> <div key={3ab}>Jason</div>
<div key={2ab}>Mike</div> <div key={1ab}>Sam</div>
<div key={2ab}>Mike</div>
Now react will Compare the results using keys and it'll figure out that only one div has been added to the top, so instead of pushing the whole array of div's to the real-DOM it will only push only one.
Recap
Frequent DOM manipulations are expensive and performance heavy.
Virtual DOM is a virtual representation of the real DOM.
When state changes occur, the virtual DOM is updated and the previous and current version of virtual DOM is compared. This is called “diffing”.
The virtual DOM then sends a batch update to the real DOM to update the UI.
React uses virtual DOM to enhance its performance.
From the docs:
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity
Consider that you're adding a list item to the beginning of a list with two items. f you're not using keys, react won't know that two elements are the same. It'll have to mutate all three items. If the items have a key, react will know that the two old items are the same, and it just has to move the two items and mutate only one item. You can read this to get a better explanation
You want it to be unique in a given list, not globally unique, just unique among its siblings.
Also, using the index as the key is generally a bad idea. The ideal key is the id or the primary key in your DB. You can also generate a hash from your data.
Everyone knows that React is faster because it uses Virtual DOM in addition to that of the actual browser DOM. This is the most important feature of React and which makes the browser work faster by updating only the changed elements without repainting the entire page. This process is called diffing. So to identify which element is changed and to achieve Diffing process a key should be provided to each and every element of an array.
Without providing a key doesn't create huge problems with rendering but it makes a mild difference in the performance of the code.
This article can help you https://medium.com/devinder/react-virtual-dom-vs-real-dom-23749ff7adc9
Finally
Need high performance and need to use the advantages of Virtual DOM -use keys. If not, don't use keys and just avoid the warning lol!!!!
The main purpose of keys is to help React differentiate and distinguish elements from each other, increasing its performance when diffing between the virtual and real DOM. To use keys, simply add the prop inside an element such as a list item
3 second google search, first result.
All:
I am pretty new to React.js, heard a lot about React virtual dom, but I kinda wondering what is the main difference between it and real DOM when deal with a simple case like change a style of a element:
Say I want to change the distance of a div to others, I can use jQuery for:
$("div#test").css("margin-left","10px");
And my understand about how browser does to the real dom is:
search down the DOM tree and find that node.
updating according style attribute( I guess it is CSSOM tree )
rendering the view
So my question is:
Is my understanding correct?
If it is basically correct, then what does virtual DOM do to improve the performance? Does not it just use a diff algorithm to compare what need to update in its light weighted version DOM tree and find that margin-left needs updating, and apply the same thing like in jquery to the real DOM?
OR
if it is not correct, could anyone give a little detail what heavy job does browser do to real DOM which React virtual DOM skip to enhance the performance?
I find a post mention 3 detail operation on virtual DOM:
What makes it really fast is:
Efficient diff algorithms.
Batching DOM read/write operations.
Efficient update of sub-tree only.
So comparing with this, does that mean in real DOM:
Not quite efficient diff algorithm or no diff algorithm(just update
everything)?
Single read/write operation like if I give 3 style updating to even same DOM element, the browser will look for that element down the DOM tree 3 times and update style?
Update whole DOM from the root(basically like dump the current tree and rebuild the whole DOM tree again no matter what part of the tree need update, and find the node and update)
Thanks
First of all, you are right with how the "real" DOM manipulation works.
React keeps an in-memory representation of the "real" DOM which we call the virtual DOM. Instead of traversing the "real" DOM for the node to modify, this virtual DOM is easily and quickly accessed therefore delivering update faster. Also, imagine a lot of changes in the DOM, traversing the "real" DOM would take a long time. This is one situation where the virtual DOM really excels.
I am using uncontrolled form input components in a project, and sometimes I need to clear the value on one input / a group of inputs. I've found that one easy way to do this is to manually update the component's key attribute.
It works, but is it bad practice? Should I be concerned about performance / memory issues when I manually change keys?
React uses keys to find "the same" element when diffing with the virtual DOM. Changing the key works because it tricks React into seeing your input as a new element. At a large scale, this will affect your performance, because React will be creating & destroying DOM elements, rather than only changing the altered attributes. At small scale, you probably won't see any noticeable performance hits.
If you're clearing the value, it seems to me you may not truly have an uncontrolled input on your hands. After all, you are "controlling" it, to some degree. However, if this is your only reason for touching the input's value, I can see why leaving it "uncontrolled" would be appealing. I haven't tested this, but I think you should be able to set the value to an empty string programmatically by giving the element a ref and accessing the DOM element that way... my assumption is that since it is uncontrolled, React doesn't pay attention to the value attribute when diffing/rendering.
I am writing new directive that is going to use ng-repeat to create nested child's and more. What I have seen so far is there are all kind of problems using this great tool of angular. You get a great binding to data, but when data increases, you are going to suffer from very low performance and defects hard to be solve. This is making the all directive very hard to maintain and reuse.
So I wanted to ask all of you who have a lot of experience with AngularJS, what are the best things to be aware of in order to use it properly and not doing huge mistakes.
Did you ever think not using it and just make your own loop?
Be glad to hear your points.
Let’s quickly recap how we can use ng-repeats (along with optional filters):
<div ng-repeat="stock in ctrl.stocks | filter:someCondition">
</div>
We can also use ng-repeats to iterate over objects (In case you hadn’t known this):
<div ng-repeat="(key, stock) in ctrl.stocksMap | filter:someOtherCondition">
</div>
where stocksMap is an object map, where the key is the id of the stock, and the value is the individual stock object. One common misunderstanding now is how AngularJS creates and displays the UI, based on this ng-repeat. This is how AngularJS works under the covers (in a nutshell) when you use ng-repeat:
It iterates over each item in the array (or each key, value in the
object)
It runs each item via any filters that are present in our
expression, to check if it should be displayed or not
It calculates a hash value by which it identifies the object (which
is by reference by default)
It checks if it has already created a DOM element for the hash value
previously If so, it reuses it
If not, it creates a DOM element based on the ng-repeat template
All the DOM manipulations are taken and inserted in an optimal
manner into the actual DOM
A watch is added on the array, which triggers step 1 again if the
array undergoes any change
For More information you can refer this LINK