Recursive rendering of components at all levels - javascript

I need help in to recursively render the components at all levels.
There are Nodes at top level and then the same structure is followed till the end.
If we get Privilege we render it differently however all Categories need to be displayed above.
Please find a fiddle here:
https://jsfiddle.net/vedvrat13/dq0u2pm7/
Expected Output:
--Category 1
----SubCategory 1
------Privilege 11
--Category 2
----SubCategory 2
------Privilege 21
------Privilege 22
Please help me with the same.
I tried multiple approaches however i am getting stuck as to how to render and call the function too.

This might help you to form the structure what you are expecting. You need to form tree structure to achieve it and find the working module here https://stackblitz.com/edit/react-65krxc?file=TreeComponent.js
renderRecursiveNodes(nodes){
return nodes.map(node => {
return (
<div>
<TreeComponent node={node} />
</div>)
});
};
render() {
return (
<div>
{this.renderRecursiveNodes(this.state.items)}
</div>
)
}
Inside your TreeComponent
render() {
let childNodes;
if (this.props.node.nodes && this.props.node.nodes.length > 0) {
console.log('test');
childNodes = this.props.node.nodes.map(function(node, index) {
return <li key={index}><TreeComponent node={node} /></li>
});
}
return (
<div>
<h5>
{this.props.node.label}
</h5>
<ul >
{childNodes}
</ul>
</div>
)
}
Please let me know if you need more info. Thanks

Fixed:
renderRecursive(level, nodes){
return nodes.map(node => {
return (
<div>
<div>{node.label}</div>
{(node.type === 'CATEGORY' && node.nodes) && this.renderRecursive(level++, node.nodes)}
</div>)
});
};

Okay this is a fun little one, but your issue is that you aren't actually looking at it the right way and as a result aren't building your markup structure correctly :P
By the looks of your Fiddle your end result actually want's to be this:
<div>
<div>
Category 1
<div>
Subcategory 1
<div>
Privilege 11
</div>
</div>
</div>
<div>
Category 2
<div>
Subcategory 2
<div>
Privilege 21
</div>
<div>
Privilege 22
</div>
</div>
</div>
</div>
Boy that was annoying to type... Anyways, so once you look at this you can see:
A parent div
Every child is wrapped in a div, and has the label and (potentially) a child.
So now we build:
... or not because you answered it XD

Related

There is a problem with implementing the map function of react

I'm sorry to ask you a simple question first. But I don't know where to fix it. I wanted to create an array of characters like a picture, so I used the map function and array, but I can't apply which part of the code is wrong. Can I know which part is wrong and how to change it? Thanks.
Footer.jsx:
this is the file I used map function and array
const array = [
'Meta', '소개', '블로그', '채용 정보', '도움말', 'API', '개인정보처리방침', '약관', '인기 계정', '해시태그', '위치', 'Instagram Lite', '연락처 업로드 & 비사용자'
]
function Footer() {
return (
<FooterWrap>
<div className='FooterWrap2'>
<div className='FooterContent1'>
<div className='FooterContent1Wrap'>
{array.map((text, idx) => {
return (
<div>
<a>
<div>
{text}
</div>
</a>
</div>
)
})}
</div>
</div>
<div className='FooterContent2'>
</div>
</div>
</FooterWrap>
)
}
export default Footer;
Why not just use a tag and its href pointing to it's link
{array.map((text, idx) => {
return (
<a href="add_url_here" key={text}>
{text}
</a>
)
})}
If you have styling issues, you can add a codesandbox link, save code and share so one can look in to it

Using values from one react component to create a ternary statement within another component

I'm going to try my best to explain my issue (I'm fairly new to all of this).
For starters. I have a component called LargestSale24H that returns a card with data from an API. Specifically it returns 3 cards; each showing one of 3 of the largest transactions in the last 24 hours. For each transaction it shows the username, user avatar, amount of transaction and date. Here is that return:
if (buyer != null) {
return (
<Container key={sale_id}>
<Card key={buyer}>
{/* <Badges account={buyer} /> */}
<UserAvatars user={buyer} />
<br></br>
<br></br>
<Link href={'/user/' + buyer}>
<Button>{buyer}</Button>
</Link>
<br></br>
<br></br>
{salePrice} {listing_symbol}
<br></br>
{parseTimestamp(updated_at_time)}
</Card>
</Container>
);
}
Additionally, I've created another component called Badges which is supposed to take each user, in this case buyer, and find all items that they own. This is what the code for badges looks like.
function Badges({ account }) {
const [assets, setAssets] = useState<Assets>();
useEffect(() => {
async function fetchData() {
const res = await fetch(
'https://proton.api.atomicassets.io/atomicassets/v1/accounts/' + account
);
const { data } = await res.json();
setAssets(data);
}
fetchData();
}, []);
if (!assets) {
return (
<div>
<Spinner />
</div>
);
}
return (
<ul>
{assets?.collections?.length > 0 &&
assets.collections.map((collectionParent, index) => {
return (
<li key={index}>{collectionParent.collection.collection_name}</li>
);
})}
</ul>
);
}
export default Badges;
Currently I have Badges commented out from LargestSale24H, when I bring it back, it lists out all the items that the user has. Items look something like 123456789. I'm listing them out only as a test buy ideally I want to create a ternary statement that says something like "if this account has this item in their collection, return A, else return B". Incase you are wondering why, it's because I want to return one time of UserAvatar if they own a specific item, otherwise, I want to keep the one that's listed.
I tried doing this:
if (<Badges account={buyer} /> == 523252144314) {
return (
<Container key={sale_id}>
<Card key={buyer}>
<UserAvatars user={buyer} />
<br></br>
<br></br>
<Link href={'/user/' + buyer}>
<Button>{buyer}</Button>
</Link>
<br></br>
<br></br>
{salePrice} {listing_symbol}
<br></br>
{parseTimestamp(updated_at_time)}
</Card>
</Container>
);
}
Now, I didn't expect that to work but since I'm still learning all this, I figured I'd give this a try. Normally I would be googling this but I don't exactly know what I have to do. I know that I'm receiving a list item per buy of what they own, that works fine. But What I need to do is basically go through each item and compare it with what's in the ternary statement basically deciding if it's true or false. I just don't know how to do that. I feel like perhaps listing the values is the incorrect thing to do. Perhaps my logic is incorrect and I should be doing something else.
In general, you can't use the jsx representation of a component in an if statement like you did.
In your Badges component you could do something like this:
return (
<ul>
{assets?.collections?.length > 0 &&
assets.collections.map((collectionParent, index) => {
if(collectionParent.collection.collection_name){
return <li key={index}>A</li>
}else{
return <li key={index}>B</li>
}
})}
</ul>
);
But that seems so obvious, that I am not sure if that is what you are asking for. In that case, I need more clarity.

Is it possible to align a div based on a condition?

I have a div which I have set to align="right". I have created a local variable called userId and assigned 1 to it and I am wondering if is possible to somehow set the div to left align if userId === 1, which will be the case. I've tried reading the react docs on conditional rendering but I don' believe that is what I'm looking for as they all deal with rendering whereas the div that I want to align is being returned by an export function so the render function isn't used.
export function MessageRow({ message, fetch }) {
return (
<div>
<br />
<div align="right" className="Message-Body">
<div className="Message-row-header">{message.user}</div>
<div>{message.content}</div>
<div className="muted-text">
(Sent: {new Date(message.timestamp).toUTCString()})
</div>
<div>
<button
className="block"
onClick={() => messageService.delete(message.id).then(fetch)}
>
Delete
</button>
<button className="block">Edit</button>
</div>
</div>
</div>
);
}
This is what I currently have and was thinking of trying a function like below but I am unsure how I would then get it to apply to the div.
function checkMessageUserID(userId) {
if (userId === 1) {
}
}
It is still being used from a render() point of view though, no?
So you could still do what you want:
return (
<div><br />
{userId !== 1 ?
<div align="right" className="Message-Body">
:
<div align="left" className="Message-Body">
}
...
1) You can return JSX from the function and call the function inside return.
function checkMessageUserID(userId) {
if (userId === 1) {
<div align="right" className="Message-Body">
}
else{
<div align="left" className="Message-Body">
}
}
Then inside return call {checkMessageUserID()}
2) You can also use ternary operator inside your render.
<div align = {userID == 1 ? "right" : "left"} className="Message-Body">
Conditional rendering works the same for functional as well as stateful components.
Working code snippet below.
<div align={userId === 1 ? 'left' : 'right' } className="Message-Body">
You could do any of the above, or what I would recommend is, you create two styling classnames, you can call them 'left/right-message' or 'mine/otherPerson-message' and you can assign align: 'left/right' to each, you can even assign different colors for that chat feel, you'd achieve this simply by doing:
<div className={userId === 1 ? "left-message" : "right-message" >
...
</div>

Passing a method down two levels

I'm trying to build a simple little game for fun, but I'm hitting a stump when it comes to passing this method down to its grandchildren. I've found similar topics, but none that answered my question. For this question, I've managed to narrow the code down to what the problem is perceived to be.
The relevant component structure is App.js > Runeboard.js > Rune.js
The Goal: To have Rune.js have an onClick function on each rune that is dynamically produced by whatever is in this.state.usersRunes.
I don't believe there is anything wrong with the function itself, or passing it down as props, because console logging the runes values all succeed when in Runeboard.js, and even in Rune.
This works, but is not dynamic to what is in the this.state.usersRunes array:
return (
<div>
Runeboard
<span onClick={() => this.props.activateRune(this.props.usersRunes[0])}> {this.props.usersRunes[0]} </span>
<span onClick={() => this.props.activateRune(this.props.usersRunes[1])}> {this.props.usersRunes[1]} </span>
<span onClick={() => this.props.activateRune(this.props.usersRunes[2])}> {this.props.usersRunes[2]} </span>
<span onClick={() => this.props.activateRune(this.props.usersRunes[3])}> {this.props.usersRunes[3]} </span>
<br />
<br />
</div>
);
The problem with that, is I'd like for every item in this.state.usersRunes (an array of integers), a Rune component that has its own onClick that successfuly executes activateRune with its parameter of the value of the rune. (The value of the rune being this.state.usersRunes So this.state.usersRunes = [2,3,5,9] the values would be 2, 3, 5, and 9.
So even though the above works, this does not and I do not understand why:
App.js
The activateRune function:
activateRune(rune) {
if (this.state.inBet || this.state.mustBet) {
this.call(rune)
} else if (!this.state.inBet) {
this.setMessage("You cannot place a rune first!")
}
}
App.js render:
<Runeboard
activateRune={this.activateRune}
usersRunes={this.state.usersRunes}
/>
Runeboard.js
render:
let rune = this.props.usersRunes.map((rune, i) => {
console.log(rune) // this works and successfully prints the array's integers
return(
<div>
<Rune activateRune={this.props.activateRune}
runeValue={rune} key={i} />
</div>
)
})
return(
<div>
{rune}
</div>
)
Rune.js
render:
return (
<div onClick={() => this.props.activateRune(this.props.runeValue)}>
{this.props.runeValue} // this works and successfully displays the value
</div>
);
How can I resolve this?
I think it might be an issue with your syntax.
<div onClick={() => {this.props.activateRune(rune)}}>
If you use curly braces, {}, you need a return value:
<div onClick={() => {return this.props.activateRune(rune)}}>
You can also avoid the curly braces and have it be:
<div onClick={() => this.props.activateRune(rune)}>
Shoutout to #xiiishaneiiix and #haopeng for the fast answers! Their comments/answer helped me try out some new things, which made me test some things and narrow what the cause was.
Turns out it was within Runeboard, in a conflict with the parameter and variable name being the same.
let rune = this.props.usersRunes.map((rune, i) => {
return(
<div>
<Rune activateRune={this.props.activateRune} runeValue={rune} key={i} />
</div>
)
})
return(
<div>
{rune}
</div>
)
Having the same name, the property I was trying to pass was actually grabbing the variable with the same name, and not the parameter from map.
The solution was in renaming the variable and parameter, clearing telling the code that we want the parameter from the map and not the variable:
let runes = this.props.usersRunes.map((runeValue, i) => {
return(
<div>
<Rune activateRune={this.props.activateRune} runeValue={runeValue} key={i} />
</div>
)
})
return(
<div>
{runes}
</div>
)

How to apply rules to only certain elements in array

I have this loop in react:
<div>
{this.state.dealersDeck.map(function(card, index){
return <div className="cardFormatDH" key={ index }> {card.rankKey}{card.suit} </div>;
}, this)}
</div>
This goes through an array of objects and then renders them on screen. This is all good except I would like to format it so I only display the contents at certain points. i.e. I'm creating blackjack and I don't want the dealers second card to be visible until the end.
I may have to show more code but was wondering if map had some sort of attribute that I could use.
You could add a boolean prop to each card and render based on that:
<div>
{this.state.dealersDeck.map(function(card, index){
return { card.display &&
<div className="cardFormatDH" key={ index }>{card.rankKey} {card.suit} </div>
}
}, this)}
</div>
You can use basic If...Else statements inside map function as well. Moreover you can write more business logic also to add more functionality.
var cardsHTML = this.state.dealersDeck.map(function(card, index){
if(condition...1){
return <div className="cardFormatDH" key={ index }> {card.rankKey}{card.suit} </div>
}
if(condition...2){
return <div></div>
}
}

Categories