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.
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.
We have a case where we need to re-mount a third party component to trigger stuff that happens in the mounting process. Not to confuse with update/rerender that we'd prefer, but we have restricted control over the component.
However, we've been searching around and found that quite many suggest using the key prop and change the value once the component should re-mount. We've tested it out and it seems to work as expected but the thing is that there is no official documentation of this approach at https://reactjs.org, and I have never seen it before. Only together with lists/iterations but not on single elements.
What do you think?
You can find an indirect answer to your question in React docs.
Keys should be stable, predictable, and unique. Unstable keys (like those produced by Math.random()) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components.
So the re-mount behavior is intended. IMHO as part of the reconciliation algorithm, it is also stable.
Using keys only makes sense in lists (arrays) inside JSX. Otherwise this shouldn't have any effect and if it does I wouldn't want to rely on this behaviour in production as I don't think it's intended
https://reactjs.org/docs/lists-and-keys.html#keys
Could you tell us what the third-party library that you are using is? Perhaps there is a better workaround for your problem!
When rendering a list, one should add a key prop to each element of the list. This is described in the documentation as a "hint" at which child elements may be stable across different renders. Changing the key's value will cause that element to be unmounted and remounted.
This behavior also currently works for any components, even ones that do not represent list elements, and is recommended by some React developers as a way of forcing any component to remount when necessary. Is this behavior, which goes beyond what is described in the documentation, a stable feature of the API that can be relied upon, or should it be viewed as it an implementation detail that is subject to change at any time?
The key attribute is intended to differentiate similar elements. Setting a new key would indeed force a re-render, but I'm not sure why you would want to take such a brute-force approach. Without knowing more about the specific situation you're encountering, it's hard to give input on whether it's a good or bad idea, but in general, it is a "hacky" implementation that is going to undo many of the benefits react provides behind the scenes in regards to it's usage of the virtual dom.
In general, if you've gotten to a point in your code where you need to force react to re-render when it isn't doing so of it's own volition, it probably means there is a more fundamental problem with your code. This would be a band aid solution to that deeper issue, even if it is a fairly stable and reliable band aid.
While going through react I came up with the following doubts:
DOM operations are very expensive
But eventually react also does the DOM manipulation. We cannot generate a view with Virtual DOM.
Collapsing the entire DOM and building it affects the user experience.
I never did that, Mostly what I do is changing the required child node(Instead of collapsing entire parent) or appending HTML code generated by JS.
Examples:
As a user scrolls down we append posts to parent element, even react
also have to do it in same way. No one collapse entire dom for that.
When a user comment on a post we append a div(comment element(HTML code)) to that particular post comment list. I think no one collapse entire post(dom) for that
3) "diffing" algorithm to check changes:
Why we need a algorithm to check changes.
Example:
If I have a 100 posts, whenever a user clicks on edit button of a particular post, i do it as follows
$(".postEdit").click(function(){
var post_id = $(this).data("postid");
//do some Ajax and DOM manipulation to that particular post.
})
I am telling the DOM to change particular element, then how does diffing help?
Am I thinking in a wrong way? If so, please then correct me.
You are correct that people don't tend to collapse and recreate the entire DOM when they need to update a single element. Instead, the best practice is to just rebuild the element you need. But what if a user takes an action that actually impacts lots of elements? Like it they star a post or something, you want to reflect that on the post and maybe in a stars count elsewhere on the page. As applications get complex enough changing all of the parts of a page that need to be changed becomes really complicated and error prone. Another developer might not be aware that there is a "stars count" elsewhere on the page and would forget to update it.
What if you could just rebuild the entire DOM? If you wrote your render methods and stored your state such that at any point, you could with certainty render the entire page from scratch? That removes a lot of these pain points, but obviously you lose all the performance gains you got from manually altering parts of the DOM by hand.
React and the virtual dom give you the best of both worlds. You get that optimized DOM updating, but as a developer you don't have to keep a mental model of the entire application and remember what you need to change for any given user or network input. The virtual dom will also potentially implement these changes more effectively than you by literally only rebuilding the elements you need. At some point you might be rebuilding more than you should "just in case".
Hope this sort of makes sense!
This discussion can be very helpful to understand Virtual DOM and it's implementation in different UI frameworks.
Why is React's concept of Virtual DOM said to be more performant than dirty model checking?
There are couple of other links as well which explains it in better way.
https://auth0.com/blog/face-off-virtual-dom-vs-incremental-dom-vs-glimmer/
http://teropa.info/blog/2015/03/02/change-and-its-detection-in-javascript-frameworks.html