Unique "key" prop in React when returning list with multiple children - javascript

I have to render multiple lists in a react component. I create my lists in the following manner:
var jobResources = null;
if (this.state.jobResources) {
jobResources = _.map(this.state.jobResources, function(jobResource, i) {
return <ul><li key={i}>{jobResource.resource.name}</li><li key={Math.floor(Math.random() * (1000 - 100)) + 100}>{translations.resourceType[jobResource.resource.resourceType.name]}</li></ul>
})
}
When it comes to rendering, I render the list thus:
<div>
<h2>Who is Working on the Project?</h2>
{this.state.jobResources ? jobResources : null}
</div>
The resulting render is as follows:
<ul data-reactid=".0.2.0.2.1:0">
<li data-reactid=".0.2.0.2.1:0.$0">Mark Smith</li>
<li data-reactid=".0.2.0.2.1:0.$270">Hair & MakeUp</li>
</ul>
<ul data-reactid=".0.2.0.2.1:1">
<li data-reactid=".0.2.0.2.1:1.$1">John Doe</li>
<li data-reactid=".0.2.0.2.1:1.$377">Photographer</li>
</ul>
As far as I see it, the keys are unique. However, react gives me the following warning:
Warning: Each child in an array or iterator should have a unique "key"
prop. Check the render method of JobDetailPage. See
http://facebook.github.io/react/docs/multiple-components.html#dynamic-children
for more information.
Could anyone tell me what is the mistake that I am making and what would be the correct way of going about this?

Never mind, figured it out. Apparently, the list element should have a key as well. The correct way of creating the list therefore would be:
var jobResources = null;
if (this.state.jobResources) {
jobResources = _.map(this.state.jobResources, function(jobResource) {
return <ul key={jobResource.id}><li key={jobResource.resource.name}>{jobResource.resource.name}</li><li key={jobResource.resource.resourceType.name}>{translations.resourceType[jobResource.resource.resourceType.name]}</li></ul>
})
}

I usually do this
if (this.state.jobResources) {
jobResources = _.map(this.state.jobResources, function(jobResource, i) {
return (
<ul>
<li key={i.toString()}>{jobResource.resource.name}</li>
<li key={`key${i}`}>
{
translations.resourceType[
jobResource.resource.resourceType.name
]
}
</li>
</ul>
);
});
}

This utility function from lodash/underscore solves it easily:
key={_.uniqueId()}

Related

is there a specific better algorithm to solve this problem faster way

I am trying to iterate through the body tag and all its children deep like if one of the children of the body contains other children, I want to be to reach those as well, I am trying to come up better and faster algorithm can anyone help to come up a better one other than mine?
<body>
<div class = "header-section">
<header class = "header-himself">
<div class = "nav-section">
<nav class = "nav-himself">
<div class = "list-section-left">
<ul class = "list-itself-left">
<li>Darster</li>
<li>Company</li>
<li>Safety</li>
<li>Help</li>
</ul>
</div>
<div class = "list-section-right">
<ul class="list-itself-right">
<li>Service</li>
<li>Dart In</li>
<li>Dart Out</li>
</ul>
</div>
</nav>
</div>
</header>
</div>
</body>
var target = document.querySelector('body');
function getChildren(target) {
if(target.children.length === 0) {
return;
}
for(var i = 0; i < target.children.length; i++) {
console.log(target.children[i].tagName);
getChildren(target.children[i]);
}
}
getChildren(target);
Here's a very simple tree walker:
const walkDOMTree = (visitor, types = [Node.ELEMENT_NODE]) => (node) => {
if (types == "*" || types .includes (node .nodeType)) {
visitor .visit (node)
}
node .childNodes .forEach (walkDOMTree (visitor, types))
return visitor
}
const visitor = (captured = []) => ({
visit: (node) => {if (node .nodeName == 'LI') captured .push (node.textContent)},
captured
})
const v = visitor ()
walkDOMTree (v) (document)
console .log ('LI content: ', v .captured)
<div class = "header-section">
<header class = "header-himself">
<div class = "nav-section">
<nav class = "nav-himself">
<div class = "list-section-left">
<ul class = "list-itself-left">
<li>Darster</li>
<li>Company</li>
<li>Safety</li>
<li>Help</li>
</ul>
</div>
<div class = "list-section-right">
<ul class="list-itself-right">
<li>Service</li>
<li>Dart In</li>
<li>Dart Out</li>
</ul>
</div>
</nav>
</div>
</header>
</div>
walkDOMTree accepts a visitor -- simply an object with a visit method -- and optionally an array of the nodeTypes you care to visit. (If you supply '*', it will visit all node types, including attributes, comments, CDATA, etc. By default, it will visit only elements.) It returns a function that takes a DOM node and then recursively calls the visitor's visit function on that node and each of its descendants in document order.
I would often write this to accept a simple function, visit, rather than an object that has such a function. But this version makes it easier for your visitor to do useful things like collecting some data, as shown here, where our tree-walker visits all elements, and the visitor collects the text of all the LI elements.
However, I wouldn't likely use such a function nowadays, as browsers already supply a built-in TreeWalker API. If this is just for learning, then please feel free to play with the above, but for production work, use the standards!
You can just use the * selector.
var allBodyNodes = document.body.querySelectorAll('*');

how do a do a scroll in to view for a newly added list item in vueJS

I am using vueJS to build a task view applicatioon
when I add a new task I want the div that contains the list to immediately focus on the newly added list item
below is my html in the template for the list of task
<ul>
<li
v-for="task in filteredTasks"
:key="task.id"
id="taskListItem"
ref="taskListItem"
class="taskList d-flex align-items-center justify-content-between"
>
<span> {{ task.name }} </span>
</li>
</ul>
below is my functions in computed and methods to add and filter out tasks
script
computed : {
filteredTasks() {
return this.selectedUsers.length
? this.filteredOnProgress.filter((task) =>
task.userIds.some((id) => this.selectedUsers.includes(id))
)
: this.filteredOnProgress;
},
}
methods : {
addTaskName(){
if (this.newTaskName.name != "") {
this.addTask(this.newTaskName)
this.newTaskName.name = ""
}
}
},
I solve my problem like this
I gave my unordered list an id="taskListul"
and in methods I added a function:
scrollToEndOfTask() {
const taskScroll = this.$el.querySelector("#taskListul")
const scrollHeight = taskScroll.scrollHeight
taskScroll.scrollTop = scrollHeight
},
I called this function in update method of the vue instance
updated() {
this.scrollToEndOfTask()
},

push element from one array to another on button click in React?

I am trying to use the method .contact() to push on element in my old_array to my new_array.
I have one button on each element in the array like this:
´´´
<li key={i}>
{{character.name} + "is" + {character.age} + "years old"}
<button onClick={this.addToNewArray}>Fav</button>
</li>
´´´
so as you can see each element got a seperate id. Now i want to click the button to push that element to a new array . (i get data from API that i .map() into my old_array) My function looks like this:
´´´
constructor(props){
super(props);
this.state = {
old_arary: [],
new_array: []
}
}
addToNewArray = () => {
let new_array = this.state.new_array.contact(this.state.old_array);
this.setState({ new_array: new_array})
}
´´´
This is where i want my output:
´´´
<li>
{this.state.new_array}
</li>
´´´
First :
in your question , you are using contact() everywhere, and I think there is no such function for array in JS :) , that should be concat()
Second :
You can use ES6 for lower code, something like this:
let new_array = [...this.state.new_array , ...this.state.old_array];
this.setState({ new_array });
Third :
Change this
<li>
{this.state.new_array}
</li>
To :
{
this.state.new_array.map((obj,index) => (
<li key={index}>
{obj.name}
</li>
))
}

How can I keep the same index of the initially rendered list when users clicked on a filtered item in React Webpack?

I have included a filter (filteredCat) for my catalogue and it works as expected visually.
The issue is that although the items are filtered, the index being logged doesn't reflect the index of the new filtered list. It returns the index of the originally rendered list.
For example if the list is filtered down to a single item whose index was initially 10. It still logs the index of 0.
export default class Catalogue extends React.Component{
productClickHandler(index){
console.log(index);
};
render(){
let filteredCat = this.props.DVDLibrary.filter(
(dvds) => {
return(
dvds.title.toLowerCase().indexOf(this.props.searchFilterInput) !== -1
)
}
);
var catologueList = filteredCat.map((dvds,index) => {
return(
<li key={index} onClick={this.productClickHandler.bind(this,index)}>
<div className="inner">
<h2>{dvds.title}</h2>
<img src={dvds.image}/>
<p><strong>Rating</strong>: {dvds.rating}</p>
<p><strong>Price</strong>: {dvds.price}</p>
<p><strong>Stock</strong>: {dvds.stock}</p>
</div>
</li>
)
});
return(
<div>
<ul>
{catologueList}
</ul>
</div>
)
}
}
How can I keep the same index of the initially rendered list when users clicked on a filtered item?
Referencing the filteredCat variable instead of the catologueList index works.

jQuery filter function is not showing exact match :(

My problem is something like this,
I am using filter function to show the matched elements(LI)in tree,its working fine but its showing all li elements which starts from passed string,for example
if i passed "Application" to filter function,then it's showing me all li, which text start from "Application"
function callme(filterData){
$("ul.treeview").find("li").hide();
if(filterData.indexOf("|")!=-1){
var filterData = filterData.split("|");
for(i=0;i<filterData.length;i++){
jQuery("ul.treeview").find("li").filter(function(index) {
return jQuery.trim($(this).text()) == filterData.trim();
}).show();
}
}else{
jQuery("ul.treeview").find("li").filter(function(index) {
return jQuery.trim($(this).text()) == filterData.trim();
}).show();
}
}
here is my html...
<ul id="leftNavigation" class="treeview">
<li>
<ul>
<li >
Application<font class="leftNavHitsFont"> - (3)</font>
</li>
</ul>
<ul>
<li >
Application Notes<font class="leftNavHitsFont"> - (1)</font>
</li>
</ul>
</li>
</ul>
Yeah, that is the definition of something containing something else.
Maybe you meant to get an exact match?
jQuery("ul.treeview").find("li").filter(function(index) {
return jQuery.trim($(this).text()) == jQuery.trim(filterData);
}).show();
jsFiddle example

Categories