Couldn't get rid of react warning of unique key in map - javascript

What's wrong with my below code? I had key={obj._id} and I expect I will not see the warning but I'm still getting it.
Warning: Each child in an array or iterator should have a unique "key"
prop. Check the render method..
renderListItems(items){
return(
<div>
{map(items, obj =>
<div key={obj._id} className="panel-body panel-row">
<div className="row">
<div className="col-md-12">
<h2 className="title">{obj.display_name}</h2>
<p className="edited">Last edited on {moment(obj.updated_at).format('DD MMM YYYY')}</p>
<div className="actions_wrap">
<Link to={`/mall-promospace/edit/${obj._id}`}>Edit</Link>
<a onClick={()=> this.setState({openConfirmationModal:true, selectedItemId: obj._id, selectedItemName: obj.display_name})}>Delete</a>
</div>
</div>
</div>
</div>
)}
</div>
)
}

I think you are coding some things wrong. You should apply the function "map" over an array.
Try this:
renderListItems(items){
return(
<div>
{items.map(obj =>
<div key={obj._id} className="panel-body panel-row">
<div className="row">
<div className="col-md-12">
<h2 className="title">{obj.display_name}</h2>
<p className="edited">Last edited on {moment(obj.updated_at).format('DD MMM YYYY')}</p>
<div className="actions_wrap">
<Link to={`/mall-promospace/edit/${obj._id}`}>Edit</Link>
<a onClick={()=> this.setState({openConfirmationModal:true, selectedItemId: obj._id, selectedItemName: obj.display_name})}>Delete</a>
</div>
</div>
</div>
</div>
)}
</div>
)
}

items.map((obj, i) => <div key={i}></div>)

Related

If - else statement not appropriately working in ReactJS

I am displaying some data from an api using the If - else condition whereby the data should be loaded, and if nothing is found, a No data Found text should be displayed
But the problem is, the No Data Found text displays immediately the page is opened. It doesn't wait for the whole data from the api to load to appear. And when the data is loaded is when it disappears or stays if the data is not there.
How do I make the No Data Found text appear only after the data is loaded and verified to be null. Thanks.
Here is my code...
var showFarmList = '';
if (farmCount >= 1) {
showFarmList = user.farm.map((farm) => {
return (
<div className="col-6" key={farm.farmid}>
<div className="card card-dull card-height">
<Link to={`/farm-details/${user.username}/${farm.farmid}`}>
<div className="card-body">
<div className="farms-card">
<h5 className="card-title title-small truncate-1">{farm.farmname}</h5>
</div>
<p className="card-text truncate-3">{farm.county.countydescription}
</p>
</div>
</Link>
</div>
</div>
)
});
}
else {
showFarmList =
<>
<div className='row'>
No Data Found
</div>
</>
}
return (
<>
<div className="appHeader no-border transparent position-absolute">
<div className="left">
<a onClick={() => navigate(-1)} className="headerButton goBack">
<i className="fi fi-rr-cross"></i> </a>
</div>
<div className="pageTitle"></div>
</div>
<div id="appCapsule" className="mt-2">
<div className="section my-farmlist">
<h2>My Farms</h2>
<p className="my-farmtext">Here is a list of all the Farms you have registered on Tunda Care</p>
<div className="row">
{isLoading && <CardSkeleton cards={2} />}
{showFarmList}
</div>
</div>
</div>
</>
);
}
export default MyFarmList;
And here is the output.
I think this is what you expecting if I understood correctly.
Add !isLoading && to second part.
<div className="row">
{isLoading && <CardSkeleton cards={2} />}
{!isLoading && showFarmList}
</div>

How to select the lowest common ancestor of elements containing specific text?

I'm not sure if "lowest common ancestor" is the right term, I also think that this problem should be quite common, I have tried to find the solutions online, but couldn't find it.
So I have below structure:
<div> <!-- A -->
<div> <!-- B -->
<div> <!-- C: I need to select this element -->
<div>
<div>
<div>
random string
<div>
<div>
SOMETHING
</div>
<div>
SOMETHING
</div>
</div>
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
</div>
<div>
random string
</div>
</div>
<div>
random string
</div>
</div>
My goal is to select that first/lowest element (in this case it's div C) that contains all children/descendants that contain string "SOMETHING".
The closest solution that I got was using xpath: //*[contains(text(),"SOMETHING")]/ancestor::*, but using this will return basically any elements that contain "SOMETHING" (it does return the div C, but also returns other elements, I only want to get the div C).
The solution doesn't have to be using xpath, but vanilla javascript is preferrable, also it doesn't have to be very efficient. Thanks in advance.
By selecting all text nodes, you can then iterate through their ancestors and keep only the one(s) that exist for all of them.
function nativeTreeWalker() {
var walker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT,
null,
false
);
var node;
var textNodes = [];
while(node = walker.nextNode()) {
textNodes.push(node);
}
return textNodes;
}
const nodes = nativeTreeWalker()
.filter(textNode => textNode.textContent.includes('SOMETH\ING'));
const getAncestors = elm => {
const set = new Set();
while (elm) {
set.add(elm);
elm = elm.parentElement;
}
return set;
};
const ancestors = nodes.map(getAncestors);
const innermostExistingInAll = [...ancestors[0]].find(
possibleParent => ancestors.every(set => set.has(possibleParent))
);
console.log(innermostExistingInAll);
<div> <!-- A -->
<div> <!-- B -->
<div id="c"> <!-- C: I need to select this element -->
<div>
<div>
<div>
random string
<div>
<div>
SOMETHING
</div>
<div>
SOMETHING
</div>
</div>
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
</div>
<div>
random string
</div>
</div>
<div>
random string
</div>
</div>
XPath 3.1 can express it declaratively:
let $text-nodes := //text()[contains(., 'SOMETHING')]
return innermost(//*[every $text in $text-nodes satisfies descendant::text() intersect $text])
XPath 3.1 is supported in the browser through the SaxonJS library from Saxonica, documented at https://www.saxonica.com/saxon-js/documentation2/index.html.
Example use
const htmlSnippet = `<div> <!-- A -->
<div> <!-- B -->
<div> <!-- C: I need to select this element -->
<div>
<div>
<div>
random string
<div>
<div>
SOMETHING
</div>
<div>
SOMETHING
</div>
</div>
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
</div>
<div>
random string
</div>
</div>
<div>
random string
</div>
</div>`;
var searchText = 'SOMETHING';
const htmlDoc = new DOMParser().parseFromString(htmlSnippet, 'text/html');
const xpathResult = SaxonJS.XPath.evaluate(
`let $text-nodes := //text()[contains(., $search-text)]
return innermost(//*[every $text in $text-nodes satisfies descendant::text() intersect $text])`,
htmlDoc,
{ params : { 'search-text' : searchText } }
);
console.log(xpathResult);
<script src="https://martin-honnen.github.io/Saxon-JS-2.5/SaxonJS2.rt.js"></script>
You could just traverse downwards from the outermost element until a descendant has more than 1 child containing the text:
let commonAnc;
let hasTxt = [document.getElementById('mainContainer')]; // or whatever outermost element you want
while(hasTxt.length == 1) {
commonAnc = hasTxt[0]; hasTxt = [];
let caChilds = commonAnc.children;
for(let i=0; i<caChilds.length; i++) {
if (caChilds[i].textContent.includes("SOMETHING")){
hasTxt.push(caChilds[i]);
}
}
}
console.log(commonAnc);
<div id="mainContainer">
<div id="a"> <!-- A -->
<div id="b"> <!-- B -->
<div id="c"> <!-- C: I need to select this element -->
<div id="d">
<div>
<div>
random string
<div>
<div>
SOMETHING
</div>
<div>
SOMETHING
</div>
</div>
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
<div>
<div>
<div>
SOMETHING
</div>
</div>
</div>
</div>
<div>
random string
</div>
</div>
<div>
random string
</div>
</div>
</div>
Feels rather inefficient, but [I think] it's the simplest way, considering that there seems to be no in-built methods for getting the closest common ancestor...

Iterate JavaScript Object Array Elements [duplicate]

This question already has answers here:
Loop inside React JSX
(84 answers)
Closed 3 years ago.
I am iterating JavaScript array elements using a for loop inside a function. Below is my array.
productArray = [
{
icon: faBox,
name: 'CHANNELS',
link: '/channelList/${channelListId}',
},
{
icon: faVideo,
name: 'VOD',
link: '/vodList/${vodId}',
},
{
icon: faMusic,
name: 'MOD',
link: null,
},
]
Following function is being used to iterate the array elements.
showProductElements = () => {
for (var i = 0; i < this.productArray.length; i++) {
return (
<Link to={this.productArray[i].link}>
<div className="tableDiv">
<div className="tableCell">
<div className="channel">
<span>
<FontAwesomeIcon icon={this.productArray[i].icon}
className="blocks-fa-icon" />
</span>
<h2 className="block-header">{this.productArray[i].name}</h2>
</div>
</div>
</div>
</Link>
);
}
};
In render method I am calling the function as follows.
<div className="row">
{this.showProductElements()}
</div>
My problem is using all these fuctions and code snippests, I can only render the first object element of the array. Can anyone help me out this for solving this problem?
In the showProductElements function, at first iteration of the for clause you return immediately. so the for loop does not see other elements of your array. You should either make an array and push elements into that and then return that array, or simply use a map function over your array and return the result.
Below is the map way (I suggest this):
showProductElements = () => {
return this.productArray.map(product => {
return (
<Link to={product.link}>
<div className="tableDiv">
<div className="tableCell">
<div className="channel">
<span>
<FontAwesomeIcon icon={product.icon}
className="blocks-fa-icon" />
</span>
<h2 className="block-header">{product.name}</h2>
</div>
</div>
</div>
</Link>
);
}});
};
And this is the Array.push way:
showProductElements = () => {
let tmpArray = [];
for (var i = 0; i < this.productArray.length; i++) {
tmpArray.push(
<Link to={this.productArray[i].link}>
<div className="tableDiv">
<div className="tableCell">
<div className="channel">
<span>
<FontAwesomeIcon icon={this.productArray[i].icon}
className="blocks-fa-icon" />
</span>
<h2 className="block-header">{this.productArray[i].name}</h2>
</div>
</div>
</div>
</Link>
);
}
return tmpArray;
};
Just don't use loop in render. Array method .map() - best choice for render array of object data
this.productArray.map(item => (
<Link to={item.link}>
<div className="tableDiv">
<div className="tableCell">
<div className="channel">
<span>
<FontAwesomeIcon icon={item.icon} className="blocks-fa-icon" />
</span>
<h2 className="block-header">{item.name}</h2>
</div>
</div>
</div>
</Link>
));
could you try this
<div className="row">
{productArray.map((product, i) =>
<Link to={product.link}>
<div className="tableDiv" key={i}>
<div className="tableCell">
<div className="channel">
<span>
<FontAwesomeIcon icon={product.icon}
className="blocks-fa-icon" />
</span>
<h2 className="block-header">{product.name}</h2>
</div>
</div>
</div>
</Link>
}
</div>
You shouldn't return in the loop - it will return from the function before the loop has a change to complete. Change it to this:
showProductElements = () => {
return this.productArray.map(product => {
return (
<Link to={product.link}>
<div className="tableDiv">
<div className="tableCell">
<div className="channel">
<span>
<FontAwesomeIcon icon={product.icon}
className="blocks-fa-icon" />
</span>
<h2 className="block-header">{product.name}</h2>
</div>
</div>
</div>
</Link>
);
}});
};
you should use map function instead using index-based for loop
<div className="row">
{
productArray.length > 0 && productArray.map((product, index) => {
return <Link to={product.link}>
<div className="tableDiv">
<div className="tableCell">
<div className="channel">
<span>
<FontAwesomeIcon icon={product.icon}
className="blocks-fa-icon" />
</span>
<h2 className="block-header">{product.name}</h2>
</div>
</div>
</div>
</Link>
})
}
</div>

React - How to resolve: "JSX expression must have parent and expression expected" error in vs code

I have a simple App.js file which is rendering fine in the browser but I am getting some problems or errors in the VS code editor.
I am getting these errors -
My App.js:
import React, { Component } from "react";
import css from "./App.less";
import DonutChart from "./DonutChart";
import TimeLineChart from "./TimeLineChart";
import DataTable from "./DataTable";
import SidebarSample from "./SidebarSample";
class App extends Component {
constructor(props) {
super(props);
}
render() {
let panelLeftCss = css.panel;
panelLeftCss += " " + css.leftPanel;
let panelRightCss = css.panel;
panelRightCss += " " + css.rightPanel;
return (
<div>
<div className="header">
<div>
<h1>Welcome Admin, My Dashboard</h1>
</div>
</div>
<div>
<SidebarSample />
</div>
<div className={css.main}>
<div className={panelLeftCss}>
<div className={css.panelHeader}>Donut Chart</div>
<div className={css.panelBody}>
<div>
<DonutChart />
</div>
</div>
</div>
<div className={panelRightCss}>
<div className={css.panelHeader}>Line Chart</div>
<div className={css.panelBody}>
<div>
<TimeLineChart />
</div>
</div>
</div>
</div>
<div>
<div className={css.panelHeader}>Data Table</div>
<div>
<DataTable />
</div>
</div>
</div>
);
}
}
export default App;
I have tried to add a <React.Fragment> also but it's not resolving the errors in the VS code editor.
Is there any tool that I can use which solves this automatically, like prettier etc?
Please guide me.
Prettier or any other code formatting tools won't help you if you do not enclose your div with tags properly.
You forgot your closing </div> tag at the end of your code.
Also you have an extra </div> tag near your <TimeLineChart> there.
Check the formatted codes here as below. It should work.
You just need to be more careful in placing the tags.
<div>
<div className="header">
<div>
<h1>Welcome Admin, My Dashboard</h1>
</div>
</div>
<div>
<SidebarSample />
</div>
<div className={css.main}>
<div className={panelLeftCss}>
<div className={css.panelHeader}>Donut Chart</div>
<div className={css.panelBody}>
<div>
<DonutChart />
</div>
</div>
</div>
<div className={panelRightCss}>
<div className={css.panelHeader}>Line Chart</div>
<div className={css.panelBody}>
<div>
<TimeLineChart />
</div>
</div>
</div>
<div>
<div className={css.panelHeader}>Data Table</div>
<div>
<DataTable />
</div>
</div>
</div>
</div>
Check your file extension its .ts
It must be .tsx(if you're using TypeScript)
Same for App.js change it to App.jsx (if you're using JavaScript)
then,
try this,
all your code must be in single parent tag, like i did inside <div>
as when render method returns only single parent, so you must careful while writing elements having only one parent or you wrap your code inside parent <div>
import React, { Component } from "react";
import css from "./App.less";
import DonutChart from "./DonutChart";
import TimeLineChart from "./TimeLineChart";
import DataTable from "./DataTable";
import SidebarSample from "./SidebarSample";
class App extends Component {
constructor(props) {
super(props);
}
render() {
let panelLeftCss = css.panel;
panelLeftCss += " " + css.leftPanel;
let panelRightCss = css.panel;
panelRightCss += " " + css.rightPanel;
return (
<div>
<div className="header">
<div>
<h1>Welcome Admin, My Dashboard</h1>
</div>
</div>
<div>
<SidebarSample />
</div>
<div className={css.main}>
<div className={panelLeftCss}>
<div className={css.panelHeader}>Donut Chart</div>
<div className={css.panelBody}>
<div>
<DonutChart />
</div>
</div>
</div>
<div className={panelRightCss}>
<div className={css.panelHeader}>Line Chart</div>
<div className={css.panelBody}>
<div>
<TimeLineChart />
</div>
</div>
</div>
<div>
<div className={css.panelHeader}>Data Table</div>
<div>
<DataTable />
</div>
</div>
</div>
</div>
);
}
}
export default App;

Ternary operator is not working in react js render function

We have a simple return render operation and we are deceding the return html using ternary operator, on the basis of state variable(anyException) value. Code snippet is shown below :
return <Form
validate={ formValidation }
onSubmit={this.onSubmit}
initialValues={initialValues}
render={({ handleSubmit, submitting, valid }) => (<form onSubmit={handleSubmit} className="k-form">
<div className="container-fixed">
(this.state.anyException ?
<ErrorDialogPopup
anyException={this.state.anyException}
errorInfo={this.state.errorInfo}
toggleErrorDialog={this.toggleErrorDialog.bind(this)}
/> : <div className="row">
{this.state.errorMessages.map(function(errorMessage) {
return <div className="errorMessage">{errorMessage}</div>
})}
</div>)
<div>
<div className="row">
<div className="col-sm-12">
<div className="panel panel-default" id="frmNetworkAdd">
<div className="panel-heading">
<h1 className="panel-title" id="panelHeader">
{this.state.networkId === -1? <span>Add Network</span> : <span>Edit Network</span>}
</h1>
</div>
But during run time, both the cases getting displayed. Could you please suggest what is going wrong here.
Instead of wrapping your ternary in (), use {} instead.
<div className="container-fixed">
{this.state.anyException ?
<ErrorDialogPopup
anyException={this.state.anyException}
errorInfo={this.state.errorInfo}
toggleErrorDialog={this.toggleErrorDialog.bind(this)}
/> : <div className="row">
{this.state.errorMessages.map(function(errorMessage) {
return <div className="errorMessage">{errorMessage}</div>
})}
</div>
}
</div>

Categories