I have a list of items that needs to be recursively rendered. I am able to render it upto first level. Can someone help me with the nested recursion. I have posted the relevant functions in a function component. Note that details has been stored in a state variable details
{
"details": [{
"title": "Part-1",
"content": [{
"type": "text",
"content": "TextABC"
}]
}, {
"title": "Part-2",
"content": [{
"type": "text",
"content": "TextXYZ"
}, {
"type": "list",
"content": [{
"text": "TextLMN",
"type": "text"
}, {
"type": "list",
"content": [{
"text": "TextPQR",
"type": "text"
}, {
"text": "TextDEF",
"type": "text"
}]
}]
}]
}, {
"title": "Part-3",
"content": [{
"type": "list",
"content": ["<a target='_blank' href='https://www.example.com'>ABC<\/a>", "<a target='_blank' href='https://www.example1.com'>XYZ<\/a>"]
}]
}]
}
Each Item is referenced either with a type that can be text or a list. I have tried the following, but the items with nested list is not working
const isAnchor = str => {
return /^\<a.*\>.*\<\/a\>/i.test(str);
};
const getContentJsx = (value) => {
return isAnchor(value) ? (
<li dangerouslySetInnerHTML={{ __html: sanitize(value) }} />
) : (
<li>{value}</li>
);
};
const getDetailJsx = () => {
return details.map(({ title, content }, index) => {
return (
<div key={`${title}${index}`}>
<h6>
<span>{title}</span>
</h6>
{content?.map(({ type: mainType, content: data }) => (
<div>
{mainType === "text" && <p>{data}</p>}
{mainType === "list" && <ul>{data?.map((contentValue) => getContentJsx(contentValue))}</ul>}
</div>
))}
</div>
);
});
};
return (
<div>
<>
{getDetailJsx()}
</>
)}
</div>
);
As suggested, you can create a recursive component that wraps the logic and makes it possible to call recursively. First, I had to correct your detail object. It has inconsistency within its attributes. Note, for example, text -> content:
"type": "text",
"text": "TextPQR"
to
"type": "text",
"content": "TextPQR"
Moreover, observe that it was separated the part that involves the title from the recursion to facilitate its comprehension and make code cleaner.
const getContentJsx = (value) => {
return isAnchor(value) ? (
<li dangerouslySetInnerHTML={{ __html: sanitize(value) }} />
) : (
<li>{recursive(value)}</li>
);
};
const getDetailJsx = () => {
return details.map(({ title, content }, index) => {
return (
<div key={`${title}${index}`}>
<h3>
<span>{title}</span>
</h3>
{recursive(content)}
</div>
)
})
}
const recursive = (content) => {
return (
<div>
{
content.map(({type: mainType, content: data}, index) => (
<div key={index}>
{mainType === "text" && <p>{data}</p>}
{mainType === "list" && <ul>{getContentJsx(data)}</ul>
}
</div>
))
}
</div>
)
Also as shown here
Related
There is a JSON for categories with the following structure
[
{
"category": "Mobiles",
"sub": [
{
"name": "Apple"
},
{
"name": "Samsung"
}
]
},
{
"category": "Televisions",
"sub": [
{
"name": "Lg"
},
{
"name": "Sony"
}
]
}
]
First i load data from backend to a variable called categories (On the backend side im using expressjs and pass data with res.json(JSON.parse(fs.readFileSync('categories.json')))
I want to iterate through categories sub category with
{categories.map(function (category, i) {
return (
<>
<h6 Key={i}>{category.category}</h6> //For example: <h6>Mobiles</h6>
<>... [logic to iterate the current category's sub categories] ...</> //For example: <p>Apple</p> <p>Samsung</p>
</>
);
})}
I tried to use a second map on category.sub like category.sub.map((s,j)=><p Key={j}>{s.name}</p>) but unfortunely i can't get it work, and I can't describe my problem to Google in English so it can be an easy answer and i am the big L
Any help?
Thanks
Try this, uncomment out the console.log to verify data if screen is white.
return (
<>
{categories.map(function (category, i) {
// console.log(category.category );
// console.log(category.sub );
return (
<>
<h6 key={i}>{category.category}</h6>
<>
{category.sub.map(function (sub, j) {
// console.log(category.category + '' + sub.name);
return <p key={j}> {sub.name}</p>;
})}
</>
</>
);
})}
</>
);
Data:
let categories = [
{
category: 'Mobiles',
sub: [
{
name: 'Apple',
},
{
name: 'Samsung',
},
],
},
{
category: 'Televisions',
sub: [
{
name: 'Lg',
},
{
name: 'Sony',
},
],
},
];
I tried to use multiple tabs in react. But it doesn't seem working (please see the picture). I 'm not able to display the header for my Tabs neither the body (from match.json) that should contained a textbox field as input...
import React from "react";
import { Tab, Tabs as TabsComponent, TabList, TabPanel } from "react-tabs";
import "react-tabs/style/react-tabs.css";
const Tabs = ({ data }) => (
<TabsComponent>
<TabList>
{data.map(({ name }, i) => (
<Tab key={i}>{name}</Tab>
))}
</TabList>
{data.map(({ name }, i) => (
<TabPanel key={i}>{name}</TabPanel>
))}
</TabsComponent>
);
export default Tabs;
MenuItemDisplay:
export default function MenuItemDisplay() {
const { match } = JsonData;
...
<Tabs data={match.questions[0]} />
</div>
</>
);
}
match.json :
{
"match": [
{
...
"_ids": [
"questions": [
{
"name": "Tell me about yourself ",
"typeProof": ""
},
...
],
]
}
]
}
The data that you want to send to the Tabs component is the currently matched item's questions array.
const { menuId, itemId } = useParams();
const { match } = JsonData;
const matchData = match.find((el) => el._id_menu === menuId)?._ids ?? [];
const item = matchData.find((el) => el._id === itemId);
Here item is the current menu item being rendered, "Pea Soup", or item "123ml66" for example:
{
"_id": "123ml66",
"name": "Pea Soup",
"description": "Creamy pea soup topped with melted cheese and sourdough croutons.",
"dishes": [
{
"meat": "N/A",
"vegetables": "pea"
}
],
"questions": [
{
"name": "Tell me about yourself ",
"typeProof": ""
},
{
"name": "Why our restaurant?",
"typeProof": ""
},
{
"name": "What hours are you available to work ?",
"typeProof": "Planning"
}
],
"invited": [
{
"name": "Jose Luis",
"location": "LA"
},
{
"name": "Smith",
"location": "New York"
}
],
"taste": "Good",
"comments": "3/4",
"price": "Low",
"availability": 0,
"trust": 1
},
Pass item.questions to the Tabs component.
<Tabs data={item.questions} />
...
const Tabs = ({ data }) => (
<TabsComponent>
<TabList>
{data.map(({ name }, i) => (
<Tab key={name}>Question {i + 1}</Tab>
))}
</TabList>
{data.map(({ name }) => (
<TabPanel key={name}>{name}</TabPanel>
))}
</TabsComponent>
);
There is a page where you can see the details of a user's loan. There is a decorator where I return values using the get () method. In general, there is a partial refund, which returns items of partial payments as shown in the photo. My problem is that I cannot specify all partial payments, only receive one by one.
Loan Details component:
<div className="ClientLoanDetails__card__content__inner__wrapper">
{Object.keys(payments[0]).map(val => {
{
[payments[0][val]].map((payment: any, index: number) => (
<div className="ClientLoanDetails__card__content-inner" key={index}>
{paymentsFields.map((item, indexInner) => (
<div className="ClientLoanDetails__card__content-item" key={indexInner}>
<div className="ClientLoanDetails__card__content-item__title">
{item.title}
</div>
<div className="ClientLoanDetails__card__content-item__value">
{payment[item.key]}
</div>
</div>
))}
</div>
));
}}
)
}}
</div>
This is code snippet for key & titles from loan.ts:
export const repaymentsFields = [
{
key: 'issuedDate',
title: lang.CLIENTS.REPAYMENTS.ISSUED_DATE,
},
{
key: 'period',
title: lang.CLIENTS.REPAYMENTS.PERIOD_IN_DAYS,
},
]
JSON of repayments:
"partialRepayments": [
{
"orderId": "A11Fz090VT1BmObJ0S-0",
"repaidPrincipalAmount": {
"amount": 250000.0
},
"repaidInterestAmount": {
"amount": 0
},
"repaidOverdueAmount": {
"amount": 0
},
"repaidProlongationAmount": {
"amount": 0
},
"started": "2020-11-09T16:52:08.981+0600",
"completed": "2020-11-09T16:52:21.170+0600",
"period": 25,
"timestamp": "2020-11-09T16:52:21.174+0600"
},
{
"orderId": "A11Fz090VT1BmObJ0S-1",
"repaidPrincipalAmount": {
"amount": 300000.0
},
"repaidInterestAmount": {
"amount": 0
},
"repaidOverdueAmount": {
"amount": 0
},
"repaidProlongationAmount": {
"amount": 0
},
"started": "2020-11-09T16:54:31.923+0600",
"completed": "2020-11-09T16:54:46.313+0600",
"period": 25,
"timestamp": "2020-11-09T16:54:46.317+0600"
}
],
the problem is that it is impossible to display the values that come as in the photo (one loan may have several repayments)
I have to return all values from an Object
IMAGE of console
Why don't you loop through the partialPayementResponse and access each field through the dot . notation ? Such as follows:
<div>
{partialRepayments.map((item, index) => {
return (
<div key={index}>
<div>Completed: <span>{item.completed}</span></div>
<div>orderId: <span>{item.orderId}</span></div>
<div>period: <span>{item.orderId}</span></div>
<div>repaidInterestAmount: <span>{item.repaidInterestAmount.amount}</span></div>
<div>repaidOverdueAmount: <span>{item.repaidOverdueAmount.amount}</span></div>
<div>repaidPrincipalAmount: <span>{item.repaidPrincipalAmount.amount}</span></div>
<div>repaidProlongationAmount: <span>{item.repaidProlongationAmount.amount}</span></div>
<div>started: <span>{item.started}</span></div>
<div>timestamp: <span>{item.timestamp}</span></div>
</div>)
})}
</div>
I wrote it without using your classNames but I think you get the idea
Not sure what's the problem here.
Can't u just iterate over these things?
Or create function and then display the thing that function returns...
for example:
displayAppropriateFields = () => {
let returnedArray = [];
returnedArray.push(<div> Some text in the div </div>);
for(let i=0; i<someArray.length; i++) {
returnedArray.push(<span> someArray[i] </span>)
}
return returnedArray;
}
And then display it in render method like so:
this.displayAppropriateFields();
In below code i am calling api by passing query parameter by map method and i want to concatenate the resulted json of each query and update the state.
But here i am facing problem that setState is updated with the any one of returned json.
import React from 'react';
const api_key = "key";
class card extends React.Component {
state = {
articles: [],
parameter: ['a', 'b', 'c', 'd', 'e']
};
componentDidMount = async () => {
this.state.newsPaper.map(async (querypara) => {
const requstone = await fetch(`https:someapisources=${querypara}&apiKey=${api_key}`);
const dataone = await requstone.json();
this.setState({
articles: dataone.articles
});
});
}
render() {
return (
<div>
<div>
{this.state.articles.map((article, index) => {
return (
<div key={index}>
<div>
<img src={article.urlToImage} alt="Avatar" />
<div>
<h4><b>{article.title}</b></h4>
<p>
{article.description}
</p>
<section>
<div>
<span>Source:</span>
<span>{article.source.name}</span>
</div>
</section>
</div>
</div>
</div>
)
})}
</div>
</div>
);
}
}
export default card;
I want to add each response json and render it by setting the state how can i do that
Below is the sample json response.
{
"status": "ok",
"totalResults": 8,
"articles": [{
"source": {
"id": "recode",
"name": "Recode"
},
"author": "Recode Staff",
"title": "title",
"description": "Something",
"url": "url",
"urlToImage": "image url.jpg",
"publishedAt": "2018-11-26T13:23:06Z",
"content": "some content"
},
{
"source": {
"id": "recode",
"name": "Recode"
},
"author": "Recode Staff",
"title": "title",
"description": "Something",
"url": "url",
"urlToImage": "image url.jpg",
"publishedAt": "2018-11-26T13:23:06Z",
"content": "some content"
}
]
}
If I understand your question correctly, then the only adjustment needed here is to update your setState() logic so that you append articles from your response to the articles array in your state:
this.state.newsPaper.map(async (querypara) => {
const requstone = await fetch(`https:someapisources=${querypara}&apiKey=${api_key}`);
const dataone = await requstone.json();
// Concatenate articles from response (dataone) to array in state
this.setState({
articles: this.state.articles.concat(dataone.articles)
});
}
How do I put my props object into an array and iterate through it?
I have a bootstrap navigation bar that has dropdowns. My code scans the javascript object and if the key dropdown is found, it will create a dropdown menu from the data in the javascript object from the dropdown section.
My Javascript Object:
var linksNav = {
items: [
{
"type": "link",
"title": "Events",
"href": "#",
"target": "_self"
},
{
"type": "link",
"title": "Groups",
"href": "#",
"target": "_self",
"dropdown":
{
"dropItems":
[
{
"left": "1st left",
"left-option": ["1","2"]
},
{
"left": "2nd left",
"left-option": ["1","2"]
},
{
"left": "3rd left",
"left-option": ["1","2"]
},
]
}
},
{
"type": "heading",
"title": "Capabilities",
"href": "#",
"target": "_self"
},
]
}
This is my LinksNav class which creates the navigation bar by going through the javascript object. If a dropdown item is detected in my javascript object, it will pass it to my Navsub component:
LinksNav:
var LinksNav = React.createClass({
render: function() {
let navDrop;
if (this.props.isDropdown) {
navDrop = (
<ul className="dropdown-menu fade">
<div>{this.props.isDropdown.dropItems[0].left}></div>
<Navsub isDropdown = {this.props.isDropdown}/>
</ul>
)
}
return (
<li className={this.props.title + ' nav-items'}>
{this.props.title}
{navDrop}
</li>
);
}
});
Navsub:
This is my Navsub class where I try to spit out the data from my dropdown list in my Javascript object if it can find it. :
var Navsub = React.createClass({
render: function() {
var itemsLeft= [];
for (var j = 0; j < this.props.isDropdown.dropItems.length; j++) {
itemsLeft.push(<Navsubrightitems key={j} type={this.props.isDropdown.dropItems[j].left} />);
}
return (
<div className="group-dropdown-menu">
<div className="dropdown-left-menu">
{this.props.isDropdown.dropItems[0].left}
{this.props.isDropdown.dropItems[1].left}
</div>
<div className="dropdown-right-menu">
{itemsRight}
</div>
</div>
);
}
});
I can successfully grab the dropdown list from the json object throuhh props, as seen by my hardcoding:
{this.props.isDropdown.dropItems[0].left}
{this.props.isDropdown.dropItems[1].left}
, but I want to iterate through it so that I get the value of left under dropItems by putting it in an array and then spitting out those values. I attempted to do so by iterating it through this.props.isDropdown.dropItems.length, but seems that's not valid as I get an undefined error.
It seems that the props dropDownItems are not initially present and hence you need to check for and undefined value and then you can use map to iterate over these. You can do it as follows
var Navsub = React.createClass({
render: function() {
return (
<div className="group-dropdown-menu">
<div className="dropdown-left-menu">
{this.props.isDropdown.dropItems[0].left}
{this.props.isDropdown.dropItems[1].left}
</div>
<div className="dropdown-right-menu">
{this.props.isDropdown.dropItems && this.props.isDropdown.dropItems.map((listItem, index) => {
return <Navsubrightitems key={index} type={listItem.left} />
})}
</div>
</div>
);
}
});
I think, this is what you want, Write it like this:
{
this.props.isDropdown.dropItems && this.props.isDropdown.dropItems.map((item,i) => {
return <Navsubrightitems key={i} type={item.left} />
})
}