I am new at React. Will be glad if someone can help:
I have parent (Dashboard) which contains all data. This data is passed to the children component (OnBoardingCard).
How can I render n times the OnBoardingCard component based on the data in the object at Dashboard without using the [num](in this case 3 times - 3x OnBoarding Cards;)?
Thank you!!
Parent- Dashboard
const cardData = [
{
svg: icon1,
title: 'Add',
content: 'add more'},
{
svg: icon2,
title: 'remove',
content: 'remove'
},
{
svg: icon3,
title: 'move',
content: 'move down'
}];
class Dashboard extends Component {
render() {
return (
<Section>
<OnboardingCard listData={cardData}/>
</Section>
);
} }
Children- OnBoardingCard
import Dashboard from "../../../../screens/Dashboard/index.js";
class OnboardingCard extends Component {
render() {
return (
<div className={styles.cardHolder}>
<div className={styles.fullCard}>
<div className={styles.onboardingCard}>
<div className={styles.iconBackground}>
<img src={this.props.listData[0].svg} />
</div>
<div className={styles.title}>{this.props.listData[0].title}</div>
</div>
<p className={styles.cardDescription}>
{this.props.listData[0].content}
</p>
</div>
</div>
); }}
When you are using a map inside render assign a unique key to its child component.
render(){
return(
{this.props.listData.map((item, i) =>
<div className={styles.cardHolder} key={i}>
<div className={styles.fullCard}>
<div className={styles.onboardingCard}>
<div className={styles.iconBackground}>
<img src={this.props.listData[0].svg} />
</div>
<div className={styles.title}>{this.props.listData[0].title}</div>
</div>
<p className={styles.cardDescription}>
{this.props.listData[0].content}
</p>
</div>
</div>
)}
);
}
You can use map function,
like this,
{this.props.listData.map((item)=>
<div className={styles.cardHolder}>
<div className={styles.fullCard}>
<div className={styles.onboardingCard}>
<div className={styles.iconBackground}>
<img src={item.svg} />
</div>
<div className={styles.title}>{item.title}</div>
</div>
<p className={styles.cardDescription}>
{item.content}
</p>
</div>
</div>)}
<Section>
<div className={styles.cardRow}>
{cardData.map((card, i) => (
<OnboardingCard {...card} key={i} />
))}
</div>
</Section>
This is what I meant (and wanted to do). So this solves my question. Thanks everyone!!
Related
I'm new in next js and I'm working on project using NextJS. I have some lots in my items page that shows lots currently(pagination/items.tsx) and I also have lotDetails page that I want it to show details of each lot using dynamic route(lotDetails\id\index.tsx).
This is the folder structure:
Now when I click the Link in Items.tsx I expect it to go to lotDetails page and pass the props, but nothing happens! (It stays on Items page!). here is Items.tsx:
import React from 'react'
import Link from "next/link"
const Items = ({currentItems}:{currentItems:any}) => {
console.log(currentItems)
// const ids=currentItems.map((el:any)=>el.id)
// const paths=ids.map((el:any)=>{params:{id:el.toString()}})
// console.log(paths)
return (
<>
<div className="container">
<div className="row">
{currentItems.map((el:any)=><div className="col-md-3 ">
//this should be linked to lotDetails page!
<Link href={{pathname:"../lotDetails/[id]",query:{id:JSON.stringify(el.id),title:el.title,image:el.image,description:el.description.toString(),rate:el.rating.rate,count:el.rating.count,price:el.price},}} as={`/lotDetails/${el.id.toString()}`}>
<div className="lot">
<div className="img-container">
<img src={el.image}/>
</div>
<div className="title">
{el.title}
</div>
<div className="price">
<span className="price-title">Price:</span>
<span>{el.price}</span>
</div>
</div>
</Link>
</div>)}
</div>
</div>
</>
)
}
export default Items;
I'm using getStaticProps and GetStaticPaths in lotDetails:
const LotDetails = (props:any) => {
const dispatch=useDispatch();
console.log(props)
const lotCount=1;
const addLots=()=>{
dispatch(AddCustomerLot({...props,lotCount:lotCount}))
}
return (
<>
<div className='container lot-details'>
<div className="row" >
<div className="col-md-6">
<div className="detail-container">
<div className="title-details"><h3>{props.title}</h3></div>
<div className="badge"><FontAwesomeIcon icon={faStar}/><span>{props.rate}</span></div>
<div className="inventory">
Inventory: <span className="count">{props.count}</span>
</div>
<div className="description-details">{props.description}</div>
<div className="price">Price: <span className="price-number">{props.price}$</span> </div>
<button className="btn btn-regist" onClick={addLots}>Add to shopping basket</button>
</div>
</div>
<div className="col-md-6"><img src={props.image} alt="" /></div>
</div>
</div>
</>
)
}
export const getStaticPaths:GetStaticPaths=async(context:any)=>{
const response= await axios.get("https://fakestoreapi.com/products")
const paths=response.data.map((el:any)=>({params:{id:el.id.toString()}}))
console.log(paths)
return{
paths,
fallback:'blocking'
}
}
export const getStaticProps:GetStaticProps=async(context:any)=>{
return{
props:
{
//recieve props
id:context.query.id,
title:context.query.title,
image:context.query.image,
description:context.query.description,
rate:context.query.rate,
count:context.query.count,
price:context.query.price
}
}
}
export default LotDetails;
when I deleted getStaticProps and getStaticPaths, the link worked! So I receive that link works perfectly and the problem is in getStaticProps and getStaticPaths. Of course I don't want to use getServerSideProps.
Update
According to julio's suggestion I changed pathName, and I also changed context.query to context.params.id:
Items:
<Link href={{pathname:`/lotDetails/${el.id}`,query:{id:JSON.stringify(el.id),title:el.title,image:el.image,description:el.description.toString(),rate:el.rating.rate,count:el.rating.count,price:el.price},}} as={`/lotDetails/${el.id.toString()}`}>
<div className="lot">
<div className="img-container">
<img src={el.image}/>
</div>
<div className="title">
{el.title}
</div>
<div className="price">
<span className="price-title">Price:</span>
<span>{el.price}</span>
</div>
</div>
</Link>
lotDetails:
return (
<>
<div className='container lot-details'>
<div className="row" >
<div className="col-md-6">
//I removed all section which used props and querys using comments
{/* <div className="detail-container">
<div className="title-details"><h3>{props.title}</h3></div>
<div className="badge"><FontAwesomeIcon icon={faStar}/><span>{props.rate}</span></div>
<div className="inventory">
Inventory: <span className="count">{props.count}</span>
</div>
<div className="description-details">{props.description}</div>
<div className="price">Price: <span className="price-number">{props.price}$</span> </div>
<button className="btn btn-regist" onClick={addLots}>Add to shopping basket</button>
</div> */}
</div>
{/* <div className="col-md-6"><img src={props.image} alt="" /></div> */}
</div>
</div>
</>
)
}
export const getStaticPaths:GetStaticPaths=async(context:any)=>{
const response= await axios.get("https://fakestoreapi.com/products")
const paths=response.data.map((el:any)=>({params:{id:el.id.toString()}}))
console.log(paths)
return{
paths,
fallback:'blocking'
}
}
export const getStaticProps:GetStaticProps=async(context:any)=>{
return{
props:
{
id:context.params.id,
// title:context.query.title,
// image:context.query.image,
// description:context.query.description,
// rate:context.query.rate,
// count:context.query.count,
// price:context.query.price
}
}
}
Finally, I solved problem in two steps:
1-I'm using google chrome browser, so I configured chrome and added my localhost port:
I used this link: chrome://inspect/, clicked configure button and added my localhost port(in my case localhost:3000)
2-I added this code to lotDetails page(the page which I used axios)
axios.defaults.httpsAgent=new https.Agent({
rejectUnauthorized:false,
})
don't forget to import:
import https from "https"
Now It's working.
I wrote down the code below.
My outcome should be 4 buttons that increment and decrement a value.
Is working but all buttons change at the same time!
The outcome I would like to get button by button and not at the same time.
I've already tried with an Array but seems I'm not on the right way!
import React from 'react';
class Counter extends React.Component {
constructor() {
super();
this.state = {
cnt: 0
};
}
handleDecrement = () => {
this.setState({
cnt: this.state.cnt + 1
});
}
handleIncrement = () => {
this.setState({
cnt: this.state.cnt - 1
});
}
render() {
return (
<><div className = "btn"></div>
<header>
<h1>Tarantino Shop</h1>
</header>
<div>
<img src= "images/walltara.png" alt="cart" width = "80%"/>
</div>
<div className="divprimario">
<div className="items">
<img src= "images/tara1.jpg" alt="cart" />
<div className = "titles"> T-Shirt Pulp Fiction</div>
<div>
<button onClick={this.handleDecrement}>+</button>
<p>{this.state.cnt} </p>
<button onClick={this.handleIncrement}>-</button>
</div>
</div>
<div className="items">
<img src= "images/tara2.jpg" alt="cart" />
<div className = "titles">T-Shirt Tarantino </div>
<div>
<button onClick={this.handleDecrement}>+</button>
<p>{this.state.cnt} </p>
<button onClick={this.handleIncrement}>-</button>
</div>
</div>
<div className="items">
<img src= "images/tara3.jpg" alt="cart" />
<div className = "titles">T-Shirt Le Iene</div>
<div>
<button onClick={this.handleDecrement}>+</button>
<p>{this.state.cnt} </p>
<button onClick={this.handleIncrement}>-</button>
</div>
</div>
<div className="items">
<img src= "images/tara4.jpg" alt="cart" />
<div className = "titles">T-Shirt Random</div>
<div>
<button onClick={this.handleDecrement}>+</button>
<p>{this.state.cnt} </p>
<button onClick={this.handleIncrement}>-</button>
</div>
</div>
</div>
</>
);
}
}
export default Counter;
So, why all the buttons change at the same time? What am I'm doing wrong?
you are only using one variable cnt to keep track of the count. If you want them to update separately, each button must increment or decrement a different state variable.
For example you could use pulpFictionCnt, tarantinoCnt etc to keep track of the different counts.
Keep a separate component for your Counter and provide other data as props.
import React from "react";
class Counter extends React.Component {
constructor() {
super();
this.state = {
cnt: 0
};
}
handleDecrement = () => {
this.setState({
cnt: this.state.cnt + 1
});
};
handleIncrement = () => {
this.setState({
cnt: this.state.cnt - 1
});
};
render() {
return (
<>
<div className="divprimario">
<div className="items">
<img src="images/tara1.jpg" alt="cart" />
<div className="titles">{this.props.title}</div>
<div>
<button onClick={this.handleDecrement}>+</button>
<p>{this.state.cnt} </p>
<button onClick={this.handleIncrement}>-</button>
</div>
</div>
</div>
</>
);
}
}
export default Counter;
Following may be some other component,
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import Counter from "./Counter";
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<div>
<div className="btn"></div>
<header>
<h1>Tarantino Shop</h1>
</header>
<div>
<img src="images/walltara.png" alt="cart" width="80%" />
</div>
<Counter title={"T-Shirt Pulp Fiction"} />
<Counter title={"T-Shirt Tarantino"} />
<Counter title={"T-Shirt Le Iene"} />
<Counter title={"T-Shirt Random"} />
</div>
</StrictMode>,
rootElement
);
Sandbox code here => https://codesandbox.io/s/laughing-bhabha-zsyvw?file=/src/Counter.js
I'm learning React and have done a fair bit of research on this. I've quickly discovered that the map() function is what I think I should be using for looping through an array.
But, my problem is all the examples in the React documentation and in the SO questions I've viewed use <ul> and <li> HTML elements to handle the output.
I'm not sure that my use case is "correct" as far as React structure is concerned, but, I want to output a <div> with some child elements each time I loop through.
Here is my static code so far:
const Comment = () => {
return (
<div className="commenter">
<div className="commenter-inner">
<div className="commenter-left">
<img src={chachi}/>
<p className="commenter-name">Scott Baio</p>
</div>
<div className="commenter-comment">
<p>Ehhhh!! Joanie loves Chachi!!!</p>
</div>
</div>
</div>
)
}
This works, but now if I have additional comments I want to be able to serve up the same block of code again but with the new commenters name, image, comment content etc.
So I've now made an array to house my multiple commenters, and things aren't really working anymore.
import React, { Component } from 'react'
import fonzie from "./img/the-fonz.jpg";
import chachi from "./img/chachi.jpg";
const Comments = [
{
id: 1,
name: 'Hello World',
photo: fonzie,
comment: 'Welcome to learning React!'
},
{
id: 2,
name: 'Hello World',
photo: chachi,
comment: 'Welcome to learning React!'
}
];
const commentEngine = props.Comments.map((comment) =>
<div className="commenter" key={comment.id}>
<div className="commenter-inner">
<div className="commenter-left">
<img src={comment.photo}/>
<p className="commenter-name">{comment.name}</p>
</div>
<div className="commenter-comment">
<p>{comment.comment}</p>
</div>
</div>
</div>
);
class Comments extends Component {
render() {
return (
<div className="comments-section col-md-10 offset-md-1 col-sm-12">
<h4>Comments</h4>
<commentEngine />
</div>
);
}
}
export default Comments
At this point I'm unsure how to verify if my loop is working in the first place and how to get the output properly displaying in my app.
Any help is greatly appreciated, as is insight into whether or not this is well structured or should be separate components.
Thanks!
It sounds like you want to re-use the Comment component with data passed by Comments. In React, this is done via props.
So, you'll want to pass the images's src, the name, and the description:
const comments = [
{
id: 1,
name: "Hello World",
photo: fonzie,
comment: "Welcome to learning React!",
},
{
id: 2,
name: "Hello World",
photo: chachi,
comment: "Welcome to learning React!",
},
];
class Comments extends Component {
render() {
return (
<div className="comments-section col-md-10 offset-md-1 col-sm-12">
<h4>Comments</h4>
{comments.map((comment) => {
return (
<Comment
key={comment.id} // https://reactjs.org/docs/lists-and-keys.html
name={comment.name}
imgSrc={comment.photo}
comment={comment.comment}
/>
);
})}
</div>
);
}
}
Notice that I've renamed the constant array Comments to comments so that the name doesn't clash with the Comments component.
Then in the Comment component, you can access these props via the argument passed to the function component:
const Comment = (props) => {
return (
<div className="commenter">
<div className="commenter-inner">
<div className="commenter-left">
<img src={props.imgSrc} />
<p className="commenter-name">{props.name}</p>
</div>
<div className="commenter-comment">
<p>{props.comment}</p>
</div>
</div>
</div>
);
};
Additionally, we can make the code a bit less verbose by leveraging object destructuring:
class Comments extends Component {
render() {
return (
<div className="comments-section col-md-10 offset-md-1 col-sm-12">
<h4>Comments</h4>
{comments.map(({ id, name, photo, comment }) => {
return (
<Comment key={id} name={name} imgSrc={photo} comment={comment} />
);
})}
</div>
);
}
}
// ...
const Comment = ({ imgSrc, name, comment }) => {
return (
<div className="commenter">
<div className="commenter-inner">
<div className="commenter-left">
<img src={imgSrc} />
<p className="commenter-name">{name}</p>
</div>
<div className="commenter-comment">
<p>{comment}</p>
</div>
</div>
</div>
);
};
const commentEngine = (comments) => {
return comments.map((comment)=>{
return (
<div className="commenter" key={comment.id}>
<div className="commenter-inner">
<div className="commenter-left">
<img src={comment.photo}/>
<p className="commenter-name">{comment.name}</p>
</div>
<div className="commenter-comment">
<p>{comment.comment}</p>
</div>
</div>
</div>
)})
class Comments extends Component {
render() {
return (
<div className="comments-section col-md-10 offset-md-1 col-sm-12">
<h4>Comments</h4>
{commentEngine(props.Comment)}
</div>
);
}
}
Now when you render Comments you need to pass the Comment props.
<Comments Comment={Comments}/>
USAGECASE
import React, { Component } from 'react'
import fonzie from "./img/the-fonz.jpg";
import chachi from "./img/chachi.jpg";
const Comments = [
{
id: 1,
name: 'Hello World',
photo: fonzie,
comment: 'Welcome to learning React!'
},
{
id: 2,
name: 'Hello World',
photo: chachi,
comment: 'Welcome to learning React!'
}
];
const Comment = props =>
const {comment} = props;
<div className="commenter" key={comment.id}>
<div className="commenter-inner">
<div className="commenter-left">
<img src={comment.photo}/>
<p className="commenter-name">{comment.name}</p>
</div>
<div className="commenter-comment">
<p>{comment.comment}</p>
</div>
</div>
</div>
);
class Comments extends Component {
render() {
return (
<div className="comments-section col-md-10 offset-md-1 col-sm-12">
<h4>Comments</h4>
{Comments.map((comment,index) => <Comment key={'[CUSTOM_KEY]'} props={comment}> )}
</div>
);
}
}
export default Comments
ANSWER
First of all, You can use index parameter in Array.map
Secondly, if you want to use list component you can make Single Component like <Comment comment={comment}> and you can use it with Array.map
And It is very good to study How to make functional component
I am using the Context API to manage state in a small app. I get the response OK, the Consumer has it, so I map through it and want to pass down data from it to a Product Card component through props. And the Product Card renders absolutely nothing. I get no errors whatsoever. Can anyone help me out please? Here is the code of parent component:
import Pagination from "./Pagination";
import { Consumer } from "../context";
export default class Browse extends Component {
render() {
return (
<Consumer>
{value => {
const { search } = value;
return (
<ProductsGrid>
{search.map(beer => (
<ProductCard
key={beer.id}
beerId={beer.id}
beerName={beer.name}
beerTagline={beer.tagline}
beerAbv={beer.abv}
firstBrewed={beer.first_brewed}
imageUrl={beer.image_url}
/>
))}
</ProductsGrid>
);
}}
</Consumer>
);
}
}`````
and here is the Product Card component:
```export default class ProductCard extends React.Component {
render() {
return (
<div className="product-card">
<div className="product-card-inner">
<div className="product-info-container">
<h1 className="beer-name">{this.props.beerName}</h1>
<p className="beer-type">{this.props.beerTagline}</p>
<p className="beer-alcohol">{this.props.beerAbv}%</p>
<p className="first-brewed">
First brewed: {this.props.firstBrewed}
</p>
</div>
<div className="beer-image-container">
<img src={this.props.imageUrl} alt="beer" />
</div>
<div className="price-container">
<h1>£ 3.50</h1>
</div>
<div className="button-container">
<div className="quantity-container">
<p className="minus-sign"> - </p>
<p className="qty-number"> 0 </p>
<p className="plus-sign"> + </p>
</div>
<div className="add-container">
<IoIosCart />
<p>Add</p>
</div>
</div>
</div>
</div>
);
}
}```
I work actually in my first React Project for a little startup, an "search and add to your collection" app. For the search feature, i use React Instant Search by Algolia. Everything work find.
Now, for the "add to collection" feature, i know how to do that, but I can not recover the data in my function. I did a little test like this:
import React from 'react';
import withAuthorization from './withAuthorization';
import * as firebase from 'firebase';
import {database, } from '../firebase/firebase';
import Image from 'react-image-resizer';
import{InstantSearch, SearchBox, Hits, Highlight, RefinementList} from "react-instantsearch/dom";
import { orderBy } from "lodash";
function addToCollection({hit}) {
console.log('-----');
console.log('The selected item is:', hit);
console.log('------');
}
const Hit = ({hit}) =>
<div className="hit">
<div className="hit-image">
<img src={hit.avatarURL} height={150} width={150}/>
</div>
<div className="hit-content">
<div className="hit-marque">
{hit.marque}
</div>
<div className="hit-numero">
{hit.numero}
</div>
<div className="hit-marquesuite">
{hit.marquesuite}
</div>
<div className="hit-reference">
{hit.reference}
</div>
<div className="hit-cote">
{hit.cote}
</div>
<button className="btn btn-success" onClick={addToCollection}>Ajouter à ma collection</button>
</div>
</div>
const Content = () =>
<div className="content container-fluid text-center">
<div className="row">
<div className="col-lg">
<Hits hitComponent={Hit} key ={Hit}/>
</div>
</div>
</div>
class Catalogue extends React.Component {
constructor(){
super();
this.state = {
catalogue: {},
};
}
render(){
if(this.state.catalogue === null) {
return <p>Le catalogue est vide</p>
}
return (
<div class="container-fluid">
<h1 className="text-center">Catalogue de capsule</h1>
<h4 className="text-center">Rechercher une capsule</h4>
<InstantSearch
apiKey="a8de2c61b98e1ca62a5df03f1970f46a"
appId="7H3CTF406R"
indexName="catalogue">
<SearchBox translation={{placeholder:'Rechercher une capsule'}} width="500 px"/>
<Content />
</InstantSearch>
</div>
);
}
}
const authCondition = (authUser) => !!authUser;
export default withAuthorization(authCondition)(Catalogue);
So, how can I transmit the information of my const "Hit" to my function "addToCollection" when I click on button.
Thank in advance for your help
<div className="hit-content">
<div className="hit-marque"> {hit.marque} </div>
<div className="hit-numero"> {hit.numero} </div>
<div className="hit-marquesuite"> {hit.marquesuite} </div>
<div className="hit-reference"> {hit.reference} </div>
<div className="hit-cote"> {hit.cote}
</div>
How can you access hit.cote and hit.avatarurl ?