when i click on the description button, all the mapped containers are showing their descriptions, but i only want the one thats pressed to show its description. so how can i make it so that the container that is pressed will show its id's description?
`import { useState } from "react";
export default function ServiciosCard({ profile }) {
const [normal, setNormal] = useState(true);
const [showDescripcion, setShowDescripcion] = useState(false);
const showDescripcionChangeHandler = () => {
setShowDescripcion(true);
setNormal(false);
};
return (
<>
<div
className=" space-y-4 mt-3 md:mt-0 h-[450px] md:h-[550px] overflow-y-auto
md:scrollbar scrollbar-track-[#d0e7d5] scrollbar-thumb-[#ef8eb2]
text-center "
>
{profile.servicios.map((servicio) => (
<>
{normal && (
<div
key={servicio._id}
className="bg-[#f7ede2] rounded-md w-[300px] h-[75px] "
>
<div>{servicio.unServicio}</div>
<div>1 Hr {servicio.costo}</div>
<div className=" space-x-4">
<button className="text-[#f77f00] hover:headerButton">
Reserva ahora
</button>
<button
onClick={showDescripcionChangeHandler(servicio._id)}
className="text-[#f77f00] hover:headerButton"
>
Descripcion
</button>
</div>
</div>
)}
{showDescripcion && (
<div
key={servicio._id}
className="bg-[#f7ede2] rounded-md w-[300px] h-[75px] "
>
{servicio.description}
</div>
)}
</>
))}
</div>
</>
);
}`
showDescription will show description for all item
you can create a new component Like below an pass srvicio to it.
import { useState } from "react";
export default function ServiciosCard({ profile }) {
return (
<>
<div
className=" space-y-4 mt-3 md:mt-0 h-[450px] md:h-[550px] overflow-y-auto
md:scrollbar scrollbar-track-[#d0e7d5] scrollbar-thumb-[#ef8eb2]
text-center "
>
{profile.servicios.map((servicio) => (
<ServicIo servicio={servicio} />
))}
</div>
</>
);
}
function ServicIo({servicio}){
const [normal, setNormal] = useState(true);
const [showDescripcion, setShowDescripcion] = useState(false);
const showDescripcionChangeHandler = () => {
setShowDescripcion(true);
setNormal(false);
};
return (<>
{normal && (
<div
key={servicio._id}
className="bg-[#f7ede2] rounded-md w-[300px] h-[75px] "
>
<div>{servicio.unServicio}</div>
<div>1 Hr {servicio.costo}</div>
<div className=" space-x-4">
<button className="text-[#f77f00] hover:headerButton">
Reserva ahora
</button>
<button
onClick={showDescripcionChangeHandler(servicio._id)}
className="text-[#f77f00] hover:headerButton"
>
Descripcion
</button>
</div>
</div>
)}
{showDescripcion && (
<div
key={servicio._id}
className="bg-[#f7ede2] rounded-md w-[300px] h-[75px] "
>
{servicio.description}
</div>
)}
</>)
}
in this code showDescription do each item that selected by user.
Related
I want to make a form that disappears if I click outside of it.
Form Component:
const CreateTaskPopup = (props) => {
const ref = query(collection(db, "tasks"));
const mutation = useFirestoreCollectionMutation(ref);
useEffect(() => {
const closeTaskPopup = (event) => {
if (event.target.id != "addForm") {
props.setTrigger(false)
}
}
document.addEventListener('click', closeTaskPopup);
return () => document.removeEventListener('click', closeTaskPopup)
}, [])
return (props.trigger) ? (
<>
<div className="bg-darkishGrey my-4 p-1 mx-3 ml-16 cursor-pointer block"
id="addForm">
<div className="flex justify-between">
<div className="flex break-all items-center ">
<Image src={fileIcon} className="w-6 mx-2"/>
<div>
<Formik
initialValues={{
taskName: "",
taskIsDone: false,
parentId: props.parentId ? props.parentId : "",
hasChildren: false,
}}
onSubmit={(values) => {
mutation.mutate(values);
props.setTrigger(false);
}}
>
<Form>
<div className="">
<TextInput
placeholder="Type a name"
name="taskName"
type="text"
/>
</div>
</Form>
</Formik>
</div>
</div>
<div className="flex items-center">
</div>
</div>
</div>
</>
) : null
}
export default CreateTaskPopup
Text Input Component:
import { useField } from "formik";
const TextInput = ({ label, ...props}) => {
const [field, meta] = useField(props);
return (
<div>
<label id="addForm" className="text-lightestGrey text-xl block"
htmlFor={props.id || props.name}>
{label}
</label>
<input id="addForm" className="bg-darkishGrey text-xl text-almostWhite my-2
outline-none w-10/12 rounded-sm p-1 mx-3" {...field} {...props} />
{meta.touched && meta.error ? <div>{meta.error}</div>: null}
</div>
);
};
export default TextInput;
I tried giving an id to the elements inside it but it's not the best solution as it has components from the Formik library to which I can't assign an id. I don't know what would be the best solution for this problem.
I have two components: MainContainer and Cart. In MainContainer, I have a button & when clicked it calls a function addToCart with an id argument, which then has to render the Cart component. I am passing that argument as a prop and then extracting the prop value in the Cart component. Wwhen I click on the button, component is not getting rendered. There are no errors as well.
MainContainer.js
import React, { useState } from "react";
import Cart from "./Cart";
import { data } from "./data";
import { Link } from "react-router-dom";
function MainContainer() {
function addToCart(id) {
return (
<div>
<Cart id={id}></Cart>
</div>
);
}
return (
<div className=" grid grid-cols-6">
{data.map((item) => (
<div
key={item.id}
className=" w-52 h-64 m-6 flex flex-col bg-gray-100 shadow-lg border-gray-200 border p-4 items-center justify-center rounded-lg relative"
>
<Link to="/cart">
{" "}
<i
className="fa-solid fa-cart-plus absolute top-3 right-3 cursor-pointer text-lg"
onClick={() => addToCart(item.id)}
></i>
</Link>
<img className=" w-32 h-32" src={item.image} alt="" />
<div className=" bg-gray-300 w-full p-2 rounded-lg mt-2 text-center">
<p className=" font-semibold text-lg"> {item.name}</p>
<p>$ {item.price}</p>
<p>{item.rating}</p>
</div>
</div>
))}
</div>
);
}
export default MainContainer;
Cart.js
import React from "react";
function Cart(props) {
return (
<div>
<h1>hi {props.id} </h1>
</div>
);
}
export default Cart;
addToCart is a callback, it can't return JSX to be rendered. You can store the id in local component state and then conditionally render the Cart component when the id state is populated.
Example:
function MainContainer() {
const [id, setId] = React.useState(); // <-- initially undefined
function addToCart(id) {
setId(id); // <-- defined
}
return (
<div className=" grid grid-cols-6">
{data.map((item) => (
<div
key={item.id}
className="...."
>
<Link to="/cart">
<i
className="...."
onClick={() => addToCart(item.id)}
/>
</Link>
<img className=" w-32 h-32" src={item.image} alt="" />
<div className="....">
<p className=" font-semibold text-lg"> {item.name}</p>
<p>$ {item.price}</p>
<p>{item.rating}</p>
</div>
</div>
))}
{id && (
<div>
<Cart id={id} /> {/* render Cart if id defined */}
</div>
)}
</div>
);
}
When I onclick in the map function, I want to know which one I clicked and only make changes to it. No matter what I did, I couldn't do it. Can you help me ?
const Product = ({categories}) => {
const [active,setActive] = useState(true)
function activeCategory(event) {
setActive(!active)
}
return (
<div className="flex flex-col">
<div className="pt-8 mx-4">
<h5 className="mx-4 text-2xl font-semibold">Kategoriler</h5>
<ul className="flex">
{categories && categories.map((category)=>(
<li onClick={(e)=>activeCategory(e)} className={active ? "p-2 bg-white shadow-lg m-2 transition duration-300 cursor-pointer" : "p-2 bg-yellow-200 shadow-lg m-2 transition duration-300 cursor-pointer"} key={category.id}>{category.title}</li>
))}
</ul>
</div>
)
export default Product;
Perhaps, You could use the category.id to identify which element is active.
const Product = ({categories}) => {
const [activeId, setActiveId] = useState()
function activeCategory(id) {
setActiveId(id)
}
function isActive(id) {
return id === activeId;
}
return (
...
{categories && categories.map((category)=> (
<li
onClick={() => activeCategory(category.id)}
className={isActive(category.id) ? "..." : ""}
key={category.id}>{category.title}
</li>
))}
...
)
}
export default Product;
you can try this code :
const Product = ({categories}) => {
const [active,setActive] = useState(0) // if you dont like one of them active , you can change value to null
return (
<div className="flex flex-col">
<div className="pt-8 mx-4">
<h5 className="mx-4 text-2xl font-mibold">Kategoriler</h5>
<ul className="flex">
{categories && categories.map((category , index)=>(
<li onClick={()=> setActive(index)} className={active == index ? "p-2 bg-white shadow-lg m-2 transition duration-300 cursor-pointer" : "p-2 bg-yellow-200 shadow-lg m-2 transition duration-300 cursor-pointer"} key={category.id}>{category.title}</li>
))}
</ul>
</div>
)
export default Product;
You should pass the parameter as an index of map (or any unique id field if available) to activeCategory like this
const Product = ({categories}) => {
const [activeId, setActiveId] = useState()
function activeCategory(id) {
setActiveId(id)
}
function isActive(id) {
return id === activeId;
}
return (
...
{categories && categories.map((category,index)=> (
<li
onClick={() => activeCategory(index)}
className={isActive(index) ? "..." : ""}
key={index}>{category.title}
</li>
))}
...
)
}
export default Product;
I have list of items = exemple1, exemple2, ..... and each one clicked
when I choose one of the list the URL be like :
localhost:3000/portfolio/exemple1
localhost:3000/portfolio/exemple2
how I can do it please?
the list
import { Link } from 'react-router-dom';
export const ListPortfolio = (props) => {
const portfolio = props.portfolio;
const number = props.number;
return(
<div className="d-flex flex-wrap table">
{portfolio.map((item, i) =>
<Link to={{ pathname:"/DetailPortfolio", state:item}} state className="text-black text-decoration-none link" key={item.id} >
<div className="card">
<img src={item.image} alt={item.title} />
<div className="card-body">
<h3 className="card-title">{item.title}</h3>
<span className="url">URL: </span><span>{item.excerpt && item.excerpt.slice(10, -5)}</span>
</div>
</div>
</Link>
)}
</div>
)
}
detailsOfList
const DetailPortfolio = (props) => {
const {state} = props.location
return (
<>
<div className="container-fluid">
<Details>
<div>
<div>{state.title}</div>
<img src={state.image} alt={state.title} />
<div>{state.content}<div/>
</div>
</div>
</Details>
</div>
</>
);
}
i have a blog post page that load content via graphql, in the content are scripts tag of charts.
the problem is that when using it does not load the scripts. Only load the scripts if you refresh the browser.
So i added the scripts to helmet after data loaded but they dont run/load .
Is there a way to "refresh" the dom?
import React, { useEffect,useState, Suspense } from "react"
import { Link, graphql } from "gatsby"
import Img from "gatsby-image"
import Layout from "../components/layout"
import EmbedContainer from "react-oembed-container"
import SEO from "../components/seo"
import { Helmet } from "react-helmet"
const PostContentComponent = React.lazy(() => import('../components/PostContentComponent'));
const BlogPost = ({ data }) => {
const [scripts,setScripts] = useState([])
function getScripts () {
// get all script tags from content
const re = /<script\b[^>]*>[\s\S]*?<\/script\b[^>]*>/g
const results = setScripts(data.strapiPost.content.match(re))
return results
}
console.log('scripts', scripts)
useEffect(() => {
getScripts()
// window.instgrm.Embeds.process()
// window.twttr.widgets.load()
}, [data])
return (
<>
<Layout>
<Helmet>
{scripts ? scripts.map((script)=> {
return script
}): null}
</Helmet>
<SEO title={data.strapiPost.title}/>
<section className="posts-container mx-auto all-blog-content my-5 sm:my-20 px-5">
<h3 className="text-1xl sm:text-3xl font-black mb-3">
{data.strapiPost.title}
</h3>
<div className="autor flex flex-wrap items-start">
<div className="autores flex ">
<div className="autorInfo flex items-start">
<h2 className="text-sm tracking-tighter text-gray-900">
By{" "}
{data.strapiPost.users_permissions_users.length === 1 ? (
<>
<Link className="hover:text-black transition duration-300 ease-in-out text-xs mr-1">
{data.strapiPost.users_permissions_users[0].username}
</Link>{" "}
</>
) : data.strapiPost.users_permissions_users.length === 2 ? (
data.strapiPost.users_permissions_users.map((x, index) => (
<>
<Link
className="hover:text-black transition duration-300 ease-in-out text-xs mr-1"
>
{x.name} {x.lastname}{" "}
{index <
data.strapiPost.users_permissions_users.length - 1
? " &"
: ""}
</Link>
</>
))
) : null}
</h2>
</div>
</div>
{/* LOAD CATEGORIES */}
<div className="md:ml-5">
<ul className="flex flex-nowrap relative ">
{data.strapiPost.categories.map(cat => {
return (
<Link
key={cat.name}
className={`bg-gray-200 py-1 px-2 mr-1 rounded-lg text-black text-xs flex-grow `}
>
{cat.name}
</Link>
)
})}
</ul>
</div>
</div>
<span className="text-gray-600 mr-3 text-xs">
Updated at {new Date(data.strapiPost.updated_at).toDateString()}
</span>
<div className="posts-content py-10">
<Img
alt={data.strapiPost.title}
key={data.strapiPost.featured_image.childImageSharp.fluid.src}
imgStyle={{ objectFit: "contain" }}
fluid={data.strapiPost.featured_image.childImageSharp.fluid}
className="mb-10"
/>
<EmbedContainer markup={data.strapiPost.content}>
<div
dangerouslySetInnerHTML={{ __html: unescape(data.strapiPost.content) }}
/>
</EmbedContainer>
</div>
{/* end of all posts */}
{/* AUTHOR CARD */}
<h3 className="text-2xl font-black text-center my-10">
Read More posts by this Author{" "}
</h3>
</section>
<section className="posts-container mx-auto">
<div
className={`grid grid-cols-1 sm:grid-cols-${data.strapiPost.users_permissions_users.length} md:grid-cols-${data.strapiPost.users_permissions_users.length} xl:grid-cols-${data.strapiPost.users_permissions_users.length} gap-4 my-5`}
>
{data.strapiPost.users_permissions_users.map((user, index) => {
return (
<div
key={index}
className="bg-purple-50 flex flex-col items-center justify-center bg-white p-4 shadow rounded-lg"
>
<div className="inline-flex shadow-lg border border-gray-200 rounded-full overflow-hidden h-40 w-40">
{/* <img
src="https://platformable.com/content/images/2020/03/headshot-profile.png"
alt=""
className="h-full w-full my-0"
/> */}
<Img
alt={data.strapiPost.title}
key={index}
fluid={user.image.childImageSharp.fluid}
className="h-full w-full my-0"
/>
</div>
<h2 className="mt-4 font-bold text-xl">
{user.name} {user.lastname}
</h2>
<h6 className="mt-2 text-sm font-medium">{user.position}</h6>
<p className="text-xs text-gray-500 text-center mt-3">
{user.bio}
</p>
</div>
)
})}
</div>
</section>
</Layout>
</>
)
}
export default BlogPost
export const query = graphql`
query MyPost($slug: String!) {
strapiPost(slug: { eq: $slug }) {
categories {
name
}
content
id
title
users_permissions_users {
id
name
lastname
username
image {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
position
}
updated_at
featured_image {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
}
`
Have you tried something like this:
<Helmet>
{scripts && scripts.map((script)=> <script>{script}</script>)}
</Helmet>
Based on: https://github.com/gatsbyjs/gatsby/issues/6299#issuecomment-402793089
Alternatively, you can use the custom hook approach (useScript).