Why can I not map keys onto React component - javascript

my render looks like this:
<ul>{liElements}</ul>
the liElements is an array of <li>word</li> created with a for-of loop.
(for the sake of argument, I do not have access to the function that creates that array)
I'm understanding the value of keys, however they are not valuable in this situation as none of the component's values will be changing, Especially since in absence of a key, "react takes the array position as key" anyway, Perfect! I want that, why does it insist on sending me a error message letting me know they are necessary, I know it doesn't know they're not necessary, be it also doesn't know they are necessary, so how bout a yellow warning?
I know I can ignore it, but it is very annoying to always have the message log to console, (especially since this message could useful elsewhere)
react.js:19287 Warning: Each child in an array or iterator should have a unique "key" prop
So since I can do
liElements[0].key // null
I thought I can just do
liElements.map(function(a,i){a.key=i;});
<ul>{liElements}</ul>
alas;
bundle.js:39123 Uncaught TypeError: Cannot assign to read only property 'key' of object '#<Object>'
Why is it readOnly? Is there anyway I can assign these totally unnecessary keys after I receive the array just so the error message stops showing?

Lets say you have a list of items you want to be li's
let items = ['item 1', 'item 2', 'item 3', 'item 4', 'item 5']
let elems = items.map( (item, index) => { return <li key={index}>{item}</li> }
You want to map your list and as you do specify a key. Why do this? because you are rendering a tree into the DOM and for React to know which exact element you are going to try and manipulate you need to pass a specific key. This way you can do things like onClick handlers.. and stuff like that.
Additionallly React uses these specific keys for performance in rendering. so even if you think its unnecessary and the end result is identical when not passing a key. It still is REALLY important, because React wont have to generate a key for every item every single render.
Finally when you go to render it.
return (
<ul>
{elems}
</ul>
)
Edit
To what Dave was saying in the comments.. Keys are created on object creation aka React.createElement(...). After their creation they are immutable. Again this is because you are creating a tree.. if you try to manually change a key then the whole tree structure would have to be 're-keyd' which is a bad idea (this is why it is read only). The key is an internal thing that react uses, but when you render dynamic elements you need to specify the key so there is no duplication of keys.
Edit 2
Heres some helpful links for you
Another Answer I Gave Regarding Keys
React Dynamic Children Documentation
Importance of Keys - Example
Dynamic elements with examples of wrong ways to do it

If you're trying to set keys, I believe you have to set them in the li elements themselves like this:
<li key={index}>list item</li>

Related

Why doesn't React use the object reference as key?

When rendering a list of items, React uses the index of each item as a default key, if none is supplied.
return <div>
{this.props.shows.map(show => <ShowComp title = {show.title}/>)}
</div>;
But why doesn't React use the JS object reference of each item instead? Seems to me like a much safer option, as it would not cause re-rendering of any of the items if the list order changes (as opposed to the index approach).
I'm relatively new to React, so I'm sure there's an underlying reason.
In Angular, ngFor (which renders a list of elements, similar to mapping object to React elements), it also has a trackBy configuration option (which is the equivalent of key in React). However, if it's not supplied, it uses the object references as an identifier, which seems more natural.
The reason why it's not using the reference of the object by default is all React's developpers choice.
Keep in mind it doesn't have to be unique across all the items, it has to be unique across the siblings of each item.
As shown here : https://reactjs.org/docs/reconciliation.html#keys
You could definitely use a not so unique key that's only unique across siblings.
Also, is using the reference of the object faster than most small integer found in unique ids ?

Accessing specific array element in an Angular2 Template

I have an array that I can loop through using ng-for syntax. However, ultimately I want to access just a single element of that array. I cannot figure out how to do that.
In my component script I have
export class TableComponent {
elements: IElement[];
}
In my template, I am able to loop through the elements via
<ul>
<li *ngFor='let element of elements'>{{element.name}}</li>
</ul>
However, trying to access an item in the element array by secifically referencing an item utilizing
x {{elements[0].name}}x
does not seem to work.
The formatting in the template is pretty explicit, so I want to be able to access each element of the array explicitly in the template.
I am not understanding something basic....
2020 Edit :
{{elements?.[0].name}}
is the new way for the null check
Original answer :
{{elements[0].name}}
should just work. If you load elements async (from a server or similar) then Angular fails when it tries to update the binding before the response from the server arrived (which is usually the case). You should get an error message in the browser console though.
Try instead
{{elements && elements[0].name}}
Work around, use ngIf check the length. elements? means if elements is null, don't read the length property.
<div *ngIf="elements?.length">
{{elements[0].name}}
</div>

What does this code using [].filter.call do?

I’m learning javascript and trying to write code that sorts a list, removing elements if they meet certain criteria.
I found this snippet that seems promising but don't have a clue how it works so I can adapt it to my needs:
list = document.getElementById("raffles-list").children; // Get a list of all open raffles on page
list = [].filter.call(list, function(j) {
if (j.getAttribute("style") === "") {
return true;
} else {
return false;
}
});
Can you guys help me learn by explaining what this code block does?
It's getting all the children of the "raffles-list" element, then returning a filtered list of those that contain an empty "style" attribute.
The first line is pretty self-evident - it just retrieves the children from the element with id "raffles-list".
The second line is a little more complicated; it's taking advantage of two things: that [], which is an empty array, is really just an object with various methods/properties on it, and that the logic on the right hand side of the equals sign needs to be evaluated before "list" gets the new value.
Uses a blank array in order to call the "filter" method
Tells the filter to use list as the array to filter, and uses function(j) to do the filtering, where j is the item in the list being tested
If the item has a style attribute that is empty, i.e. has no style applied, it returns true.
Edit:
As per OP comment, [].filter is a prototype, so essentially an object which has various properties just like everything else. In this case filter is a method - see here. Normally you just specify an anonymous function/method that does the testing, however the author here has used the .call in order to specify an arbitrary object to do the testing on. It appears this is already built into the standard filter method, so I don't know why they did it this way.
Array like objects are some of javascript objects which are similar to arrays but with differences for example they don't implement array prototypes. If you want to achieve benefits of array over them (for example like question filter children of an element )you can do it this way:
Array.prototype.functionName.call(arrayLikeObject, [arg1, [arg2 ...]]);
Here in question array like is html element collection; and it takes items without any styling.
list is assigned a collection of elements that are children of the raffles-list element
list is then reassigned by filtering its elements as follows
an empty array is filtered by calling it with the parameter list and a callback function. The formal parameters for call are this (which is the list) and optionally further objects (in this case a callback function)
The callback function receives a formal parameter j and is called for each element
If the element's value for the style attribute is empty the element is retained in the array. Otherwise it is discarded.
At the end list should contain all elements that don't have a value for its style attribute

Detect array mutation

I have made a simple proof-of-concept Polymer 1.0 app that demonstrates my problem: JSBin.
In my problem, I am using array mutation methods to alter the array, which contains the list of shopping items.
However, this doesn't seem to work as intended. I do get a change in dom-repeat and when printing the length of the array. But I do not get the change event when I am printing the array itself nor when I wrap it in a function.
In short, why does this work?
<p>Number of items: [[list.length]]</p>
And why does this not work?
<p>Items inline: [[list]]</p>
<p>Observe function : [[_observe(list)]]</p>
Also, when I uncomment the following line (in the JSBin), things seem to work as indened. But I don't like it since it's a bit hackish.
app.notifyPath('list', app.list.slice());
I have stumbled upon the slice() fix by reading this issue: https://github.com/Polymer/polymer/issues/2068
EDIT
So, after reviewing the comments, the answer to the question "Is this by design" is YES. The array itself doesn't change (since it's only a reference), but it's property do change. That's why the slice() forces the reload since it creates a shallow copy.
However, one could argue whether this is OK. Yes, the variable list does not change per se. But putting [[list]] in the HTML code actually triggers toString(). And result of that function has changed.
I guess I'm stuck with piggybacking the length property for now...
As alluded to in the comments the notifyPath and slice calls are creating a shallow copy of the array and assigning a different reference back to the list variable - triggering an update of the binding. Without maintaining a separate (watchable) variable or messing around with object references, the only other workaround I can think of would be to piggy back on the list.length property instead of the list itself and pass that through some kind of "formatting" function. e.g.
<p>Items inline: [[format(list.length)]]</p>
app.format = function(){
return app.list.toString();
};
» Fiddle
As pointed out by #zb you could expand on this and make the function reusable with any array by passing the relevant variable as an argument too:
<p>Items inline: [[format(list, list.length)]]</p>
app.format = function(list){
return list.toString();
};
» Fiddle

Firebase deep querying

This question is about implementing firebase deep querying. Consider the following structure in firebase:
Here, my ref is pointing to the root of the structure which is /messages. So I have :
var ref = new Firebase("https://cofounder.firebaseio.com/messages");
I wish to query those message Id's having member = -752163252 . So basically the returned object should be the one with key 655974744 . How do I go about doing this in firebase?
Here's what I tried in my console:
ref.orderByChild("members").equalTo(235642888).on('value', function(snap){console.log("Found ",snap.val())});
Result was:
VM837:2 Found null
I sense there is a missing link somewhere. Basically, I want to know if there is any way to query the deep nested data without having the parent key id's (in this case 25487894,655974744) .
Also, if I may extend my question, is there a way to add a listener that calls back when a new messageId (in this case 25487894,655974744) is added containing member = -752163252 .
Hope my question is clear enough. Any help is greatly appreciated!
EDIT:
I have already looked at the dinosaurs example, and that's how I tried what I tried but it didn't work.
Your query asserts that the "members" node is an integer with value 235642888, which it is not, naturally. It's an object containing a list of keys.
Instead, you would want to use the deep child query syntax and something like the following:
ref.orderByChild("members/235642888").equalTo(235642888);
Note that you don't really need the value of the key to be the key itself. You could save storage by just setting this to a binary true or 1. The query would be much the same:
ref.orderByChild("members/235642888").equalTo(true);

Categories