How can I fix CSS modules copy of styles problem? - javascript

I'm using css modules on my react app (Shopify hydrogen). I have product card component and I use it in loop to render. Problem is duplicate of styles. These components have same classnames but I see duplicate of styles. They are totally same. How can I fix it?
https://i.stack.imgur.com/MnT3L.png
Loop
const HomePage: React.FC = () => {
return (
<div className="text-center mt-8">
<div className="container">
<SimpleGrid>
{data.map((product, index) => (
<ProductCard key={index} {...product} />
))}
</SimpleGrid>
</div>
</div>
);
};
Product Card
import React from "react";
import cn from "classnames";
import styles from "./product-card.module.css";
export interface IProductCardProps {
title: string;
brand: string;
image: string;
price: number;
lastSale?: number;
currency?: string;
}
const ProductCard: React.FC<IProductCardProps> = ({
title,
brand,
image,
price,
lastSale,
currency,
}) => {
return (
<div className={styles.productCard}>
<div>
<img src={image} className={styles.productCardImage} />
</div>
<div className={styles.productCardContent}>
<div className="text-sm text-gray-600">{brand}</div>
<div className="text-md">{title}</div>
<div className="text-lg font-semibold mt-2">
{price} {currency}
</div>
<div>
{lastSale ? (
<div
className={cn(
lastSale - price > 0 ? "text-gray-500" : "text-red-500"
)}
>
Last sale: {lastSale} {currency}
</div>
) : null}
</div>
</div>
</div>
);
};
ProductCard.defaultProps = {
currency: "$",
};
export default ProductCard;
Css
.product-card {
#apply border
hover:border-black;
&__image {
#apply w-full h-full p-8 object-contain;
}
&__content {
#apply flex flex-col p-2.5;
}
}

Related

How to properly conditionally render child component in react

I have the following components : (AddBookPanel contains the AddBookForm)
I need to have the ability to display the form on click of the 'AddBookButton', and also
have the ability to hide the form while clicking the 'x' button (img in AddBookForm component), however the component doesn't seem to load properly, what could be wrong here? What do you think is the best way to organize this?
import { AddBookButton } from './AddBookButton';
import { AddBookForm } from './AddBookForm';
import { useState } from 'react';
export const AddBookPanel = () => {
const [addBookPressed, setAddBookPressed] = useState(false);
return (
<div>
<div onClick={() => setAddBookPressed(!addBookPressed)}>
<AddBookButton />
</div>
<AddBookForm initialFormActive={addBookPressed} />
</div>
);
};
import { useState } from 'react';
interface AddBookFormProps {
initialFormActive: boolean;
}
export const AddBookForm: React.FC<AddBookFormProps> = ({
initialFormActive,
}) => {
const [isFormActive, setIsFormActive] = useState(initialFormActive);
return (
<>
{isFormActive && (
<div className="fixed box-border h-2/3 w-1/3 border-4 left-1/3 top-24 bg-white border-slate-600 shadow-xl">
<div className="flex justify-between bg-slate-400">
<div>
<p className="text-4xl my-5 mx-6">Add a new Book</p>
</div>
<div className="h-16 w-16 my-3 mx-3">
<button onClick={() => setIsFormActive(!isFormActive)}>
<img
src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/OOjs_UI_icon_close.svg/1200px-OOjs_UI_icon_close.svg.png"
alt="close-button"
className="object-fit"
></img>
</button>
</div>
</div>
<div className="flex-col">
<div className="flex justify-between my-10 ml-5 text-xl">
<input type="text" placeholder="book name"></input>
</div>
</div>
</div>
)}
</>
);
};
You're always rendering the AddBookForm. That means in the initial render the useState is called with false. Since you never call setIsFormActive it is now stuck as "do not render".
Just don't use the useState and use the property initialFormActive directly. And maybe rename it to isFormActive. Let the parent handle the state change.
import { AddBookButton } from './AddBookButton';
import { AddBookForm } from './AddBookForm';
import { useState } from 'react';
export const AddBookPanel = () => {
const [addBookPressed, setAddBookPressed] = useState(false);
return (
<div>
<div onClick={() => setAddBookPressed(!addBookPressed)}>
<AddBookButton />
</div>
<AddBookForm initialFormActive={addBookPressed} onClose={() => setAddBookPressed(false)} />
</div>
);
};
import { useState } from 'react';
interface AddBookFormProps {
initialFormActive: boolean;
onColse: () => void;
}
export const AddBookForm: React.FC<AddBookFormProps> = ({
initialFormActive,
onClose,
}) => {
// const [isFormActive, setIsFormActive] = useState(initialFormActive); don't use this frozen state, allow the parent to control visibility
return (
<>
{initialFormActive && (
<div className="fixed box-border h-2/3 w-1/3 border-4 left-1/3 top-24 bg-white border-slate-600 shadow-xl">
<div className="flex justify-between bg-slate-400">
<div>
<p className="text-4xl my-5 mx-6">Add a new Book</p>
</div>
<div className="h-16 w-16 my-3 mx-3">
<button onClick={() => onClose()}>
<img
src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/OOjs_UI_icon_close.svg/1200px-OOjs_UI_icon_close.svg.png"
alt="close-button"
className="object-fit"
></img>
</button>
</div>
</div>
<div className="flex-col">
<div className="flex justify-between my-10 ml-5 text-xl">
<input type="text" placeholder="book name"></input>
</div>
</div>
</div>
)}
</>
);
};
Problem with your code is in how you tied initialFormActive to inner state of AddBookForm component. When looking at useState inside AddBookForm it is clear that prop initialFormActive only affects your initial state, and when looking at your parent component I can clearly see that you passed false as value for initialFormActive prop, thus initializing state inside AddBookForm with false value(form will stay hidden). Then you expect when you click on button, and when prop value changes(becomes true), that your <AddBookForm /> will become visible, but it will not because you just used initial value(false) from prop and after that completely decoupled from prop value change.
Since you want to control visibility outside of component, then you should attach two props to AddBookForm, isVisible and toggleVisibiliy. And do something like this:
export const AddBookPanel = () => {
const [addBookPressed, setAddBookPressed] = useState(false);
const toggleAddBookPressed = () => setAddBookPressed(p => !p);
return (
<div>
<div onClick={() => setAddBookPressed(!addBookPressed)}>
<AddBookButton />
</div>
<AddBookForm isVisible={addBookPressed} toggleVisibility={toggleAddBookPressed } />
</div>
);
};
export const AddBookForm: React.FC<AddBookFormProps> = ({
isVisible, toggleVisibility
}) => {
return (
<>
{isVisible&& (
<div className="fixed box-border h-2/3 w-1/3 border-4 left-1/3 top-24 bg-white border-slate-600 shadow-xl">
<div className="flex justify-between bg-slate-400">
<div>
<p className="text-4xl my-5 mx-6">Add a new Book</p>
</div>
<div className="h-16 w-16 my-3 mx-3">
<button onClick={toggleVisibility}>
<img
src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/OOjs_UI_icon_close.svg/1200px-OOjs_UI_icon_close.svg.png"
alt="close-button"
className="object-fit"
></img>
</button>
</div>
</div>
<div className="flex-col">
<div className="flex justify-between my-10 ml-5 text-xl">
<input type="text" placeholder="book name"></input>
</div>
</div>
</div>
)}
</>
);
};

Hydration error when fetching data from array of objects

I am using Nextjs and there is a sidebar in which I am trying to get random 5 posts from an array of objects. The defined function is working fine and displaying 5 posts but I am getting a Hydration error showing Prop alt did not match. When I tried to display it on the console the alt value is different.
import Link from 'next/link';
import Image from 'next/image';
import { BlogData } from '../data/blogdata';
function getMultipleRandom() {
const shuffled = [...BlogData].sort(() => 0.5 - Math.random());
return shuffled.slice(0, 5);
}
const Sidebar = () => {
return (
<>
<h2 className='font-roboto text-3xl font-semibold pb-10'>Featured Posts</h2>
{
getMultipleRandom().map((val) => {
return (
<div key={val._id} className='flex flex-col pt-5'>
<div className='w-56 pr-5'><Image src={val.featuredImage} alt={val.alt} width={1200} height={800} className=' rounded-3xl' /></div>
<Link href={`/blog/${val.slug}`}><a><h3 className='text-sm font-poppins font-medium hover:text-[#5836ed] transition-all duration-300'>{val.title}</h3></a></Link>
</div>
);
})
}
</>
)
}
export default Sidebar;
try this code :
import Link from "next/link";
import Image from "next/image";
import { BlogData } from "../data/blogdata";
import { useEffect, useState } from "react";
const Sidebar = () => {
const [shuffled, setShuffled] = useState([]);
const [loading, setLoading] = useState(true); // anyting you want !!!
useEffect(() => {
if (loading) {
const x = [...BlogData].sort(() => 0.5 - Math.random());
setShuffled(x.slice(0, 5));
setLoading(false);
}
}, [loading]);
return (
<>
<h2 className="font-roboto text-3xl font-semibold pb-10">
Featured Posts
</h2>
{loading ? <div>loading ... </div> : shuffled.map((val) => {
return (
<div key={val._id} className="flex flex-col pt-5">
<div className="w-56 pr-5">
<Image
src={val.featuredImage}
alt={val.alt}
width={1200}
height={800}
className=" rounded-3xl"
/>
</div>
<Link href={`/blog/${val.slug}`}>
<a>
<h3 className="text-sm font-poppins font-medium hover:text-[#5836ed] transition-all duration-300">
{val.title}
</h3>
</a>
</Link>
</div>
);
})}
</>
);
};
export default Sidebar;

Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider> while using useselector

1.header.js
import Image from "next/image";
import {
MenuIcon,
SearchIcon,
ShoppingCartIcon,
} from "#heroicons/react/outline";
import { signIn, signOut, useSession } from "next-auth/react";
import { useRouter } from "next/router";
import { useSelector } from "react-redux";
import { selectItems } from "../slices/basketSlice";
import { Provider } from "react-redux";
function Header() {
const { data: session } = useSession();
const router = useRouter();
const items= useSelector(selectItems);
return (
<header>
<div className="flex items-center bg-amazon_blue p-1 flex-grow py-2">
<div className="mt-2 flex items-center flex-grow sm:flex-grow-0">
<Image
onClick={() => router.push("/")}
src='https://links.papareact.com/f90'
width={150}
height={40}
objectFit="contain"
className="cursor-pointer"
/>
</div>
{/*search bar*/}
<div className="hidden sm:flex items-center h-10 rounded-md flex-grow cursor-pointer bg-yellow-400 hover:bg-yellow-500">
<input className="p-2 h-full w-6 flex-grow flex-shrink rounded-l-md focus:outline-none px-4" type="text" />
<SearchIcon className="h-12 p-4"/>
</div>
<div className="text-white flex items-center text-xs space-x-6 mx-6 whitespace-nowrap">
<div onClick={session ? signIn:signOut} className="link">
<p className="hover:underline">{session ? session.user.name:"signin"}</p>
<p className="font-extrabold md:text-sm">Acount & lists</p>
</div>
<div className=" link">
<p>Returns</p>
<p className="font-extrabold md:text-sm">& orders</p>
</div>
<div onClick={() => router.push("/checkout")} className=" link relative flex items-center">
<span className="absolute top-0 right-0 md:right-10 h-4 w-4 bg-yellow-400 rounded-full text-center text-black font-bold">
{items.length}
</span>
<ShoppingCartIcon className="h-10 "/>
<p className="hidden md:inline font-extrabold md:text-sm mt-2">Bascket</p>
</div>
</div>
</div>
{/*bottom nav*/}
<div className="flex items-center space-x-3 p-2 pl-6 bg-amazon_blue-light text-white text-sm">
<p className="link flex items-center ">
<MenuIcon className="h-6 mr-5"/>
all</p>
<p className="link">featured </p>
<p className="link">new arrival </p>
<p className="link">catalog </p>
<p className="link hidden lg-inline-flex">electronics </p>
</div>
</header>
);
}
export default Header;
bascketslice.js
'''
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
items: [],
};
export const basketSlice = createSlice({
name: "basket",
initialState,
reducers: {
addToBasket: (state, action) => {
state.items = [...state.items, action.payload]
},
removeFromBasket: (state, action) => {},
},
});
export const { addToBasket, removeFromBasket } = basketSlice.actions;
// Selectors - This is how we pull information from the Global store slice
export const selectItems = (state) => state.basket.items;
export default basketSlice.reducer;
'''
product.js
'''
import Image from "next/image";
import {useState} from "react";
import{ StarIcon} from"#heroicons/react/solid";
import Currency from "react-currency-format";
import { useDispatch } from "react-redux";
import {addToBasket} from "../slices/basketSlice";
const MAX_RATING =5;
const MIN_RATING =1;
function Product({id, title, price, description, category,image}) {
const dispatch = useDispatch();
const{rating}= useState(
Math.floor(Math.random() * (MAX_RATING - MIN_RATING +1)) + MIN_RATING
);
const addItemToBascket = () => {
const product = {id, title, price, description, category,image};
// sending the product as an action to bascket slice
dispatch(addToBasket(product))
};
return (
{category}
<Image src={image} height={200} width={200} objectFit="contain" />
<h4 className="flex">{title}</h4>
<div className="flex">
{Array(rating)
.fill()
.map((_, i)=>(
<StarIcon className="h-5 text-yellow-500"/>
))}
</div>
<p className="text-xs my-2 line-clamp-2">{description}</p>
<div className="mb-5">
<Currency quantity={price}/>
</div>
<button onClick={addItemToBascket} className=" mt-auto button">Add to Basket</button>
</div>
);
}
export default Product
'''
store.js
import { configureStore } from "#reduxjs/toolkit";
import basketReducer from "../slices/basketSlice";
export const store = configureStore({
reducer: {
basket: basketReducer,
},
});
_app.js
import { Provider } from 'react-redux'
import { store } from '../app/store'
import '../styles/globals.css'
import { SessionProvider } from "next-auth/react"
const MyApp = ({ Component, pageProps: {session,...pageProps} }) => {
return (
<SessionProvider session={session}>
<Component {...pageProps} />
</SessionProvider>
);
};
export default MyApp
how do i use provider in the code
my repository :- https://github.com/Shadow2389/nmz-2.git
It seems we're both fans of Sonny. Your problem there is that you need to wrap up your App.js in
<Provider>
<SessionProvider session={session}>
<Component {...pageProps} />
</SessionProvider>
</Provider>
But then you'll most likely have a "Hydration error", if so, just remove the "Math.floor(Math.random() * (MAX_RATING - MIN_RATING +1)) + MIN_RATING"
Apparently with the new updates we can no longer use the useState() hook as we used to, so you have 3 options:
1.- Remove the "Math.floor(Math.random() * (MAX_RATING - MIN_RATING +1)) + MIN_RATING"
2.- Set a fixed rating for all products
3.- Set a number for your API (which you cannot, since you are using FakeStoreAPI)
This is due to basic rules of Hooks, since a Random number is "umpredictable", but now I don't know why it has that problem (although you're setting a min and a max)

Tailwind CSS Grid Spacing Messed Up

I am trying to make a blog website with two columns for the posts. The first column displays one large-format post while the second displays 3 small-format posts (pictured below). However, when i do this to small-format posts seem to respect the spacing of the large-format post, even though they are in different columns. Here is a picture:
As you can see, I want the posts on the right side to be spaced evenly, but the second post starts at the end of the large-format post on the first column.
Here is my code:
import React, { useEffect, useState } from 'react'
import client from '../client'
import BlockContent from '#sanity/block-content-to-react'
import { Link } from 'react-router-dom'
function Main() {
const [posts, setPosts] = useState([])
useEffect(() => {
client.fetch(
`*[_type == "post"] {
title,
slug,
body,
author,
mainImage {
asset -> {
_id,
url
},
alt
},
publishedAt
}`
).then((data) => setPosts(data))
.catch(console.error)
}, [])
return (
<div className='grid lg:grid-cols-3 md:grid-cols-2 gap-8 m-4 '>
{posts.slice(0, 1).map((p, i) => (
<Link to = {`/blog/${p.slug.current}`} className=''>
<article key = {p.slug.current} className=''>
<img src = {p.mainImage.asset.url} alt = {p.title} className='' />
<div>
<p className='font-bold text-xl text-secondary'>{p.title}</p>
<p className='text-sm'>By Brandon Pyle | {new Date(p.publishedAt).toLocaleDateString()}</p>
</div>
</article>
</Link>
))}
{posts.slice(1, 4).map((p, i) => (
<Link to = {`/blog/${p.slug.current}`} className='col-start-2 h-16'>
<article key = {p.slug.current} className='flex'>
<img src = {p.mainImage.asset.url} alt = {p.title} className='w-auto h-auto max-h-[80px]' />
<div>
<p className='font-bold text-xl text-secondary'>{p.title}</p>
<p className='text-sm'>By Brandon Pyle | {new Date(p.publishedAt).toLocaleDateString()}</p>
</div>
</article>
</Link>
))}
</div>
)
}
export default Main
Please let me know if you have any ideas on how to fix this issue! Thanks.
try build your cols with this code with the size you want to
grid-cols-[80px_400px_1fr_250px]=grid-cols-[first_second_therd]
and check this too Try build layout with tailwind CSS
I figured out what was causing the issue. All I had to do was wrap each of the map functions in a div, like so:
import React, { useEffect, useState } from 'react'
import client from '../client'
import BlockContent from '#sanity/block-content-to-react'
import { Link } from 'react-router-dom'
function Main() {
const [posts, setPosts] = useState([])
useEffect(() => {
client.fetch(
`*[_type == "post"] {
title,
slug,
body,
author,
mainImage {
asset -> {
_id,
url
},
alt
},
publishedAt
}`
).then((data) => setPosts(data))
.catch(console.error)
}, [])
return (
<div className='grid lg:grid-cols-3 md:grid-cols-2 gap-8 m-4 '>
<div>
{posts.slice(0, 1).map((p, i) => (
<Link to = {`/blog/${p.slug.current}`} className=''>
<article key = {p.slug.current} className=''>
<img src = {p.mainImage.asset.url} alt = {p.title} className='' />
<div>
<p className='font-bold text-xl text-secondary'>{p.title}</p>
<p className='text-sm'>By Brandon Pyle | {new Date(p.publishedAt).toLocaleDateString()}</p>
</div>
</article>
</Link>
))}
</div>
<div className='my-[-16px]'>
{posts.slice(1, 4).map((p, i) => (
<Link to = {`/blog/${p.slug.current}`} className='col-start-2'>
<article key = {p.slug.current} className='flex my-4'>
<img src = {p.mainImage.asset.url} alt = {p.title} className='w-auto h-auto max-h-[80px]' />
<div>
<p className='font-bold text-xl text-secondary'>{p.title}</p>
<p className='text-sm'>By Brandon Pyle | {new Date(p.publishedAt).toLocaleDateString()}</p>
</div>
</article>
</Link>
))}
</div>
</div>
)
}
export default Main

Error arising in my code. I am using React, NPM and styled components. What are the steps I need to take?

import React, { Component } from "react";
import Product from "./Product";
import Title from "./Title";
import { ProductConsumer } from "../context";
export default class Productlist extends Component {
render() {
return (
<React.Fragment>
<div className="py-5">
<div className="container">
<Title name="our" title="products" />
<div className="row">
<ProductConsumer>
{value => {
return value.products.map(product => {
return <Product key={product.id} product={product} />;
});
}}
</ProductConsumer>
</div>
</div>
</div>
</React.Fragment>
);
}
}
Okay, I am getting this error and I am not quite sure what it is since I am so new to working with React.JS. could someone assist me in figuring out what may be happening.
export default class Product extends Component {
render() {
const { id, title, img, price, inCart } = this.props.product;
return (
<ProductWrapper className="col-9 mx-auto col-md-6 col-lg-3 my-3">
<div className="card">
<div
className="img-container p-5"
onClick={console.log("you clicked me on the image container")}
>
<Link to="/details">
<img src={img} alt="product" className="card-img-top" />
</Link>
</div>
</div>
</ProductWrapper>
);
}
}
const ProductWrapper = styled.div
Above is the Product code as asked! Thank you!

Categories

Resources