I am creating pages dynamically per product id and I am able to access the data via pageContext within each page. The product pages are generating correctly with the data expected, however I am struggling with the logic of passing the data to other components eg the shopping cart. How do I pass the data across?
gatsby-node.js
const products = require('./src/helpers/product.json');
products.forEach(product => {
createPage({
path: `/product/${product.productCode}/`,
component : require.resolve('./src/templates/product.js'),
context : {
title: product.title,
description: product.description,
image: product.image,
price: product.price,
productCode: product.productCode,
}
})
})
The Products page has various components such as QuickView of the item, MiniCartView, add to Cart- all of which I would like to share the product data across the components.
Product.js (reduced code snippet)
const Product = ({ pageContext }) => {
const ctxAddItemNotification = useContext(AddItemNotificationContext);
const showNotification = ctxAddItemNotification.showNotification;
return (
<Layout>
<div className={styles.root}>
<Container size={'large'} spacing={'min'}>
<Breadcrumbs
crumbs={[
{ link: '/', label: 'Home' },
{ label: `${pageContext.name}` },
]}
/>
<div className={styles.content}>
<div className={styles.gallery}>
<Gallery images={pageContext.gallery} />
</div>
<div className={styles.details}>
<h1>{pageContext.name}</h1>
<div className={styles.actionContainer}>
<div className={styles.addToButtonContainer}>
<Button
onClick={() => { showNotification() }}
fullWidth
level={'primary'}
setProductID={setProductID}>
Add to Bag
</Button>
</div>
</div>
</div>
}
I am using a template and trying to add functionality but unable to work out how to pass data through to the other components. The miniCart component has hardcoded mock data but I want to pass in the data per product which was retrieved from the json file.
MiniCart.js
const MiniCart = ({props}) => {
const sampleCartItem = {
image: '',
alt: '',
name: 'name',
price: 220,
color: 'Anthracite Melange',
size: 'xs',
};
return (
<div className={styles.root}>
<div className={styles.titleContainer}>
<h4>My Bag</h4>
</div>
</div>
....
The <MiniCart> reference is within another component (header.js). Levels above the Product.js, therefore I was unable to use to pass the selected product's data across.
header.js
<Drawer visible={showMiniCart} close={() => setShowMiniCart(false)}>
<MiniCart />
</Drawer>
If anyone is able to help I would greatly appreciate :)
Related
I am building a web app with next.js and am using the PowerBI client to render an iframe with statistics. I can interact with the iframe via the library to update filters and pages. This means the iframe should NOT rerender everytime a prop changes.
The parent component looks like this.
const Dashboard: NextPage = ({ data }) => {
const [activePage, setActivePage] = useState(null);
const DynamicDashboard = dynamic(
() =>
import("../components/dynamicDashboard").then(
(dashboard) => dashboard.default
),
{ ssr: false }
);
return (
<>
<Header setActivePage={setActivePage} />
<DynamicDashboard data={data} activePage={activePage} />
</>
);
};
The header (child 1) looks like this:
export default function ({ setActivePage }) {
const menuItems = [
{
displayName: "page1",
name: "ReportSection5ac5ce426571489b0038",
},
{
displayName: "page2",
name: "ReportSectionf3695be54d17769ba015",
},
{
displayName: "page3",
name: "ReportSection60bb35142132a98c6b86",
},
];
const menuItemClickHandler = (name) => {
setActivePage(name);
};
return (
<>
<header className={styles.header}>
<div className={styles.inner}>
<div className={styles.logo}>
<img src="/assets/images/logo.svg" alt="logo" />
</div>
<ul className={styles.menu}>
{menuItems.map((item) => (
<li key={item.name}>
<a href="#" onClick={() => menuItemClickHandler(item.name)}>
{item.displayName}
</a>
</li>
))}
</ul>
</div>
</header>
</>
);
}
DynamicDashboard (child 2) receives the prop "activePage" like this.
To keep everything simple and focussed on the question I'll keep the contents of this component empty.
export default function DynamicDashboard({ data, activePage }) {}
I understand that the rerender happens because I'm using useState.
However when I want to use useRef I get the message that I'm not allowed to use a ref on a function component.
So basically I want to update the DynamicDashboard component based on events in the Header component without rerendering the whole component.
I'm developing a Products page using React. I have setup data like this
const data = {
items: [
{
id: 1,
name: 'Alloha Zune',
brand: 'Alloha',
slug: 'asus-zune-4',
price: '90$',
image: '/images/alloha_zune.png',
category: 'Alloha',
},
{
id: 2,
name: 'Bacleim Pro',
brand: 'Bacleim',
slug: 'bacleim-pro',
price: '110$',
image: '/images/bacleim_pro.jpg',
category: 'Bacleim',
},
{
id: 3,
name: 'Xarin Makeshift',
brand: 'Xarin',
slug: 'xarin-makeshift',
price: '120$',
image: '/images/xarin_makeshift.png',
category: 'Xarin',
},
],
};
export default data;
I want to iterate these items as cards using reusable components.
The card component is as follows:
export default function Card() {
return (
<div>
<>
<div>
<img
src={item.image}
alt={item.name}
/>
<div>
<h2>{item.name}</h2>
<p>{item.price}</p>
<ul>
<p>{item.brand}</p>
<p>{item.category}</p>
</ul>
<button type="submit" className="primary-button">
Buy
</button>
</div>
</div>
</>
</div>
);
}
And I want to create another component such CardList Component to iterate over the data.
What will be the most code-efficient way to do this in React?
Create a reusable Product Component and you can pass data to it in the form of props, like this
return (
<div>
{data.items.map(product => {
return <ProductComponent ...product />
})}
</div>
)
Note I'm spreading the product object so that all the key-value pairs are passed to the component in the form of props
You can map over the data.items and pass required props to Card component.
CODESANDBOX DEMO
export default function App() {
return (
<>
{data.items.map((product) => {
const { id, ...rest } = product;
return <Card {...rest} key={id} />;
})}
</>
);
}
I'm learning to react and wanted to create a card that, when clicked, routes the user to an external web page. I haven't seen any tutorials or explanations on this all I keep finding is for internal website navigation. I tried doing this in different ways but can't find the correct way to do this.
my component:
import React from "react";
function Card (props) {
const handleClick = () => {
return
}
return (
<div>
}
<div className="link-container" onClick= {()=> {
return handleClick();}
}>
<div className="row">
<div className="card">
<hr className="divide"></hr>
<img className="img" src={props.img} alt="social-icon" />
<h4 className="name">{props.name}</h4>
</div>
</div>
</div>
</div>
)
}
export default Card;
href links to be used:
const links = [
{
id: 1,
name: "Youtube",
img:"./img/youtube1.svg",
href: "https://www.youtube.com/c/SubwaySounds"
},
{
id: 2,
name: "Spotify",
img:"./img/spotify.svg",
href: "https://artists.spotify.com/c/artist/3DM32kjsG7Pp5EM7o3Uv7t/profile/overview"
},
{
id: 3,
name: "Tiktok",
img:"./img/tiktok.svg",
href: "https://www.tiktok.com/#nysubwaysounds"
},
{
id: 4,
name: "Instagram",
img:"./img/Instagram1.svg",
href: "https://www.instagram.com/nycsubwaysounds/?hl=en"
},
{
id: 5,
name: "Shop",
img:"./img/shop.svg",
href: "https://my-store-11524143.creator-spring.com/"
}
]
export default links;
What you need to do is to create an element for each item for the list. For this, you can use the builtin JS map function.
For example, you can create a new variable holding the elements:
const linksElements = links.map(item => (<Card item={item} />));
and use it with {linksElements} inside of the return value.
I've created this as an example using your list: https://stackblitz.com/edit/react-yt1hfl?file=src/App.js. Note that this is a quick and dirty implementation, without any other components.
Using React, I have created a component named ItemPoke.
Then I use array.map to read my data from a small array of 4 objects.
The structure is dasplay but not the images and the content of h3 and span labels. Using the dev tool from Chrome I can see the props value, the strigs are correctly asignated, but the image and info are not displayed.
My code:
const pokemons = [
{
name: "bulbasaur",
url: "https://pokeapi.co/api/v2/pokemon/1/",
sprite:
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png",
type: "water"
},
{
name: "ivysaur",
url: "https://pokeapi.co/api/v2/pokemon/2/",
sprite:
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/2.png",
type: "water"
},
{
name: "venusaur",
url: "https://pokeapi.co/api/v2/pokemon/3/",
sprite:
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/3.png",
type: "water"
},
{
name: "Charmander",
url: "https://pokeapi.co/api/v2/pokemon/3/",
sprite:
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/6.png",
type: "fire"
}
];
export default pokemons;
import '../App.css';
function ItemPoke({NamePoke,TypePoke,ImagePoke}) {
return (
<article className='list-pokemons-item'>
<div className='list-pokemons-item-content'>
<img
src={ImagePoke}
alt="pokemon-1"
></img>
<h3>{NamePoke}
<span>{TypePoke}</span>
</h3>
</div>
</article>
);
}
export default ItemPoke;
import '../App.css';
import ItemPoke from '../components/ItemPoke';
import pokemons from '../components/data';
export default function ListPoke() {
const ListaPokemons = pokemons.map ((item, index) => {
return(
<ItemPoke key = {index} name= {item.name} type = {item.type} image = {item.sprite} />
)
}
)
return (
<main className="list-pokemons">
{ListaPokemons}
</main>
);
}
You need to match the prop names you passing to your component:
function ItemPoke({ name, type, image }) {
return (
<article className="list-pokemons-item">
<div className="list-pokemons-item-content">
<img src={image} alt="pokemon-1"></img>
<h3>
{name}
<span>{type}</span>
</h3>
</div>
</article>
);
}
I am trying to create a form using react-json schema-form. I am new to the custom templates for the same. I would like to have all the widgets in the form in a single row. How to do that ?
i have tried the following (component) , which was from the custom Object from their website, but couldn't get the desired result.
import React from 'react';
import Form from 'react-jsonschema-form';
/* this is my schma*/
const AdHocCheckSchema = {
title: "search",
type: "object",
required: ["searchKeyword", "country"],
properties: {
searchKeyWord: {
type: "string",
title: "Search Keyword"
},
country: {
type: "string",
title: "country",
enum: [
"a",
"b"
],
enumNames: [
"a",
"b"
]
}
}
};
/*this is the ui schema*/
const adHocCheckUiSchema = {
"ui:order": [
"searchKeyWord",
"country"
],
"country": {
"ui:widget": "select"
}
};
function CustomTemplate(props)
{
return (
<div>
{props.title}
{props.description}
{props.properties.map(
element =>
<div className="property-wrapper">{element.content}</div>)}
</div>
);
}
const AdHocCheckComponent = () => {
return (
<Form
className="tp-adhoccheck__horizontal"
schema={AdHocCheckSchema}
uiSchema={adHocCheckUiSchema}
CustomTemplate={CustomTemplate}
/>
);
};
export default AdHocCheckComponent;
i have no idea how to make the input field , select widget and also the button in same line. As of now its looking as in a default form one line after another.
You can customize the look and feel of each field via their templates. Given that the form submits as an object, you'd want to tweak the ObjectFieldTemplate:
https://react-jsonschema-form.readthedocs.io/en/latest/advanced-customization/#object-field-template
In fact, if you go to their playground (https://mozilla-services.github.io/react-jsonschema-form/, "Custom Object" tab link on top), you'll see all the fields in a single row (if your screen resolution is high enough, otherwise they will wrap over into subsequent rows). Their source code for that effect (via a custom ObjectFieldTemplate component( is located here: https://github.com/mozilla-services/react-jsonschema-form/blob/master/playground/samples/customObject.js
function ObjectFieldTemplate({ TitleField, properties, title, description }) {
return (
<div>
<TitleField title={title} />
<div className="row">
{properties.map(prop => (
<div
className="col-lg-2 col-md-4 col-sm-6 col-xs-12"
key={prop.content.key}>
{prop.content}
</div>
))}
</div>
{description}
</div>
);
}
i used customFieldTemplate and flex-box and could make it in a row
export const customFieldTemplate = (props) => {
const {id, label, classNames, required, errors, children} = props;
return (
<div className={classNames}>
<label className="field_label" htmlFor={id}>
<span className="required-field">
{required ? '*' : null}
</span>
{label}
</label>
{children}
{errors}
</div>
);
};
I am using typescript and in my case here is the answer
function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
return (
<div>
<h3>{props.title}</h3>
<p>{props.description}</p>
{props.properties.map((element) => (
<div className='property-wrapper'>{element.content}</div>
))}
</div>
);
}
<Form
formData={formState}
schema={schema as JSONSchema7}
transformErrors={transformErrors}
onChange={(e) => setFormState(e.formData)}
validator={validator}
onSubmit={() => onSubmit}
templates={{ ObjectFieldTemplate }}
>
Ref:
https://react-jsonschema-form.readthedocs.io/en/latest/advanced-customization/custom-templates/