Passing a method down two levels - javascript

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>
)

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

"Each child in a list should have a unique "key" prop" [duplicate]

I'm building an app using the Google Books API and I appear to be passing a unique key to each child in the list, but the error won't go away. I must be doing something wrong but I'm not sure what.
const BookList = (props) => {
//map over all of the book items to create a new card for each one in the list
const books = props.books.data.items.map((book) => {
console.log(book.id);
return (
<div className="col col-lg-4 grid-wrapper">
<BookCard
key={book.id}
image={book.volumeInfo.imageLinks.thumbnail}
title={book.volumeInfo.title}
author={book.volumeInfo.authors[0]}
description={book.volumeInfo.description}
previewLink={book.volumeInfo.previewLink}
buyLink={book.saleInfo.buyLink}
/>
</div>
);
});
return <div>{books}</div>;
};
Notice that after the return in const books I have a console.log(book.id), which will display all 10 unique id keys in the console. But when I try to pass it to the child of this component using key={book.id}, I get this error.
The key needs to go on the outermost returned element. In your specific case, that means changing this:
<div className="col col-lg-4 grid-wrapper">
<BookCard
key={book.id}
to this:
<div className="col col-lg-4 grid-wrapper" key={book.id}>
<BookCard
I was using React fragments in my map() call in their simple syntax form, and was running into the same warnings with the code below:
<>
<h3>{employee.department}</h3>
<TableRow
key={employee.id}
cellValues={["Name", "Title"]} />
<TableRow
key={employee.id}
cellValues={[employee.name, employee.title]}
/>
</>
Building off the accepted answer, I realized I needed the outermost element to have the ID. I learned of an alternate syntax for React fragments that allows one to put an ID on it. The resulting code below caused the warnings to go away:
<React.Fragment key={employee.id}>
<h3>{employee.department}</h3>
<TableRow
cellValues={["Name", "Title"]} />
<TableRow
cellValues={[employee.name, employee.title]}
/>
</React.Fragment>

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.

Can we pass fake keys to children in React.js?

I have created a component and it's running well in local server. But I am getting below warning
Warning: Each child in a list should have a unique "key" prop.
Getting this warning means we need to fix the key index props? as given here.
below is some snippets of my component code..
render() {
return (
<div>
<Container>
<Row>
<Col className="col-12">
{this.state.client.map((val,index)=>{
if(index == this.state.colaborators.length -1)
return <a href={"/users/"+val}>{val}</a>
return <a href={"/users/"+val}>{val} ,</a>
})}
</Col>
</Row>
</Container>
</div>
</div>
</div >
);
}
}
export default App;
I checked some solution from here
As I told my code is working well. Can we use some fake key props? for example
key={fake index}
And we are using will this affect in my working code?
If this.state.client ever changes, don't just use the index (which is sadly common); see this article for why and its demo of what can go wrong. You can only do that with a list that never changes, or only grows/shrinks (and not at the same time), not with one where the order changes (you insert at the beginning, or sort, or...) More in the docs.
I'm guessing val will be unique in the list, so use that as the key:
{this.state.client.map((val, index) => {
const href = "/users/" + val;
const display = index == this.state.colaborators.length - 1 ? val : `${val} ,`;
return <a key={val} href={href} >{display}</a>;
})}
If your lists order is not going to change, simply use:
return <a key={index} href={"/users/"+val}>{val}</a>
return <a key={index} href={"/users/"+val}>{val} ,</a>
It will not affect your code and it will remove the warning.

How to inject a dinamically created element into an existing div in React JSX?

I have a list of objects photos, from a json data file, that I would like to organize into 3 different <div> columns, but I dont know how to achieve that, here is my broken non-optimized code:
<div className="container">
<div ref={leftColRef} className="left-col" />
<div ref={centreColRef} className="centre-col" />
<div ref={rightColRef} className="right-col" />
{Object.keys(photos).forEach((n, i) => {
const id = photos[n].id;
const thumb = photos[n].thumbnailUrl;
const title = photos[n].title;
const element = (
<Thumbnail id={id} title={title} thumb={thumb} />
);
if (i % 3 === 0) {
leftColRef.current.append(element);
} else if (i % 3 === 1) {
centreColRef.current.append(element);
} else {
rightColRef.current.append(element);
}
// this line works, it idsplays the data but is commented as the data needs to go inside its respective columns
// return <Thumbnail key={id} title={title} thumb={thumb} />;
})}
</div>
The idea is to insert some elements into the left-column when i%3 = 0 and others in the centre-column when i%3 = 1 and so on ...
And a link to my codesandbox
Any help/advise will be much appreciated.
Easiest is probably to prepare the data outside the render function and to render the column one by one.
You should not manipulate the DOM like it's done in jQuery using JSX
Example:
const Component = (props) => {
const filterPhotos = (column) => {
return props.photos.filter((photo,index)=> index%3==column);
}
return <>
<MyColumn photos={filterPhotos(0)}/>
<MyColumn photos={filterPhotos(1)}/>
<MyColumn photos={filterPhotos(2)}/>
</>;
}
First, using ref on div to inject stuff on it is wrong. It's the opposite of how react works.
Like charlies said, I would split the photos in 3 different arrays before the render. Then, you'll be able to do something like this :
<div ref={leftColRef} className="left-col" />
{ photosLeft.map(photo => <Thumbnail key={photo.id} {...photo} />)
</div>
when preparing your data, try to use the same object properties and component props name so you can spread it easily ( {...photo} ).
Note: Also, when rendering an array in react, each child must have a unique key props. It will help react to render on that part of dom if your data change.

Categories