I'm using react with sanity io to create a portfolio page. I would like to create an unordered list according to how many tags I have a on a project. A project is composed of a title, image, description, and tags(this is just an array).
What I have tried is:
{project.tags.map(type => {
return (
<li>{type.type.name}</li>
)
})}
And I keep getting the error: "TypeError: project.forEach is not a function". If I just render
{project.tags}
all of the tags are displayed but then I can't style them hence the need to put them into a list.
Does anyone know how to accomplish this?
import React, { useEffect, useState} from "react";
import sanityClient from "../client.js";
import '../index.css';
export default function Project() {
const [projectData, setProjectData] = useState(null);
useEffect(() => {
sanityClient.fetch(`*[_type == "project"] | order(index) {
title,
mainImage{
asset->{
_id,
url
},
},
description,
projectType,
link,
tags
}`).then((data) => setProjectData(data)).catch(console.error);
}, []);
return (
<div className="antialiased text-gray-800 bg-gray-400">
<div className="container relative flex flex-col px-6 mx-auto space-y-8">
<div className="absolute inset-0 z-0 w-2 h-full bg-white shadow-md left-38 md:mx-auto md:right-0 md:left-0"></div>
{projectData && projectData.map((project, index)=> (
<div className="relative z-10 p-10 ">
<img src={project.mainImage.asset.url} alt={project.mainImage.alt} className="timeline-img" />
<div className={`${index % 2 === 0 ? "timeline-container-left timeline-container" : "timeline-container" }`} >
<p className="font-bold uppercase">{project.title}</p>
<div className={`${index % 2 === 0 ? "timeline-pointer-left timeline-pointer" : "timeline-pointer" }`} aria-hidden="true"></div>
<div className="p-6 bg-white rounded-md shadow-md sm:p-2">
<p className="pt-1">{project.description}</p>
</div>
</div>
</div>
))}
</div>
</div>
)
}
{project.tags.map( type => ( {type} ))} is the answer.
Related
I am implementing a modal in my react app but face the weird error, Error: Expected `onClick` listener to be a function, instead got a value of `object` type.. What's confusing here is the fact that I have implemented the modal in another part of the application using the same logic and it doesn't produce such error.
Below are important snippets of the code.
index.js
import React, { useState } from "react";
import ProfileSave from "../../components/modal/profileSave";
const test = () => {
const [saveModal, setSaveModal] = useState(false);
const [save, setSave] = useState(false);
return (
<>
<button className="w-[165px] h-[60px] text-center px-4 py-2 text-sm text-white bg-[#3A76BF] rounded-md font-bold text-[16px]" onClick={saveShowModal}>
Save
</button>
{saveModal && (
<ProfileSave
open={saveModal}
handleClose={() => setSaveModal(!saveModal)}
handleSave={handleSave}
/>
)}
</>
export default test
profileSave.js
import React from "react";
const ProfileSave = (open, handleClose, handleModal) => {
return open ? (
<div className="fixed inset-0 z-10 overflow-y-auto">
<div
className="fixed inset-0 w-full h-full bg-black opacity-40"
onClick={handleClose}
></div>
</div>
) : (
""
);
};
export default ProfileSave;
You neet to extract the props that you component recive
Try this :
import React from "react";
const ProfileSave = ({open, handleClose, handleModal}) => {
return open ? (
<div className="fixed inset-0 z-10 overflow-y-auto">
<div
className="fixed inset-0 w-full h-full bg-black opacity-40"
onClick={handleClose}
></div>
</div>
) : (
""
);
};
export default ProfileSave;
I get the error "Uncaught TypeError: DroppedItem.map is not a function ReactJS" when trying to show a floating window on my page after opening a case. I think it has something to do with renders (https://jsramblings.com/are-you-logging-the-state-immediately-after-updating-it-heres-why-that-doesnt-work/) but I got no idea how to fix it. Please help. I use Axios to load arrays from my backend server. The "className" is from TailWind.
import {useEffect, useState, useContext} from "react"
import {useLocation} from 'react-router-dom'
import Axios from "axios";
import { AppContext } from "/src/App";
export default function CaseDetail(){
const [ContentList, setContentList] = useState([])
const [Case, setCase] = useState([])
const [ShowDrop, setShowDrop] = useState(false)
const [DroppedItem, setDroppedItem] = useState([])
const { token } = useContext(AppContext);
const { setToken } = useContext(AppContext);
const { userID } = useContext(AppContext);
const DroppedItemFunc = (array) => {
setDroppedItem(array)
}
const DropWindow = () => {
setShowDrop(!ShowDrop)
}
const MilSpecList = ContentList.filter(function(driver){
return driver.RarityName === "Mil-Spec"
})
const openCase = (CasePrice) => {
const Price = parseInt(CasePrice)
if (token >= Price){
setToken(token-Price)
const randomNumero = Math.random()* 100;
if(randomNumero <= 70)
{
const millength = MilSpecList.length
const randomItemM = Math.floor(Math.random() * (millength - 0)+ 0)
console.log(MilSpecList[randomItemM].SkinName)
console.log(MilSpecList[randomItemM].IDItem)
submitItem(MilSpecList[randomItemM].IDItem)
setDroppedItem(MilSpecList[randomItemM])
setShowDrop(!ShowDrop)
}
}else{console.log("No funds")}
}
return(
<div className="flex justify-center align-bottom">
{ShowDrop && <div className=" absolute">
{DroppedItem.map(item => (
<div className=" border-2 border-slate-500 w-[25rem] h-[30rem] p-1.5 px-2 m-2 box-content rounded-cool bg-slate-700 ">
<button
className=' border-2 border-green-700 bg-green-400 hover:bg-green-600 text-green-900 hover:text-rose-50 w-[100%] h-10 rounded-cool '
onClick={()=>{DropWindow()}}
>
Close
</button>
</div>
))}
</div>
}
<div className="bg-slate-800 rounded-cool text-white divide-y-2 w-3/5 p-3">
{Case.map(item => (
<h1 key={item.CaseName} className="text-4xl font-bold text-slate-200 py-2" >
{item.CaseName} 💼
</h1>
))}
{Case.map(item => (
<div key={item.CaseName} className="flex justify-center p-3 mt-2">
<div className=" w-[50%] p-1.5 px-2 m-2 box-content rounded-cool bg-slate-700 "
>
<div className="flex justify-center">
<img className="" src={`src/images/${item.CaseImage}`}/>
</div>
<h1 className="hover:text-orange-300">
{item.CaseName}
</h1>
<h1 className="hover:text-orange-300 basis-1/2">
{item.CasePrice} Tokens
</h1>
<div className=" flex justify-center">
<button className=" cursor-pointer text-xl text-slate-900 w-40 h-10 hover:text-orange-300 hover:bg-slate-600 hover:border-2 hover:border-white-200 bg-orange-300 rounded-cool px-2 py-0.25 transition-all"
onClick={() => {openCase(item.CasePrice)}}
>
OPEN
</button>
</div>
</div>
</div>
))}
</div>
</div>
)
}
your error is here: setDroppedItem(MilSpecList[randomItemM]) - you cannot modify the data type of your state from an array, to some other value, as the method 'map' no longer becomes available. Try to make sure you do not modify data structures, I find that prop-types (when in reactJS), typescript (when in reactTS), and eslint (in general) help me a lot to make sure I don't accidentally mutate my structure type on accident. Try also console logging the value and type of MilSpecList[randomItemM] to confirm the type is indeed an array, as well as anywhere you might be running setDroppedItem
Im working on a custom toast notification. But the hard part to me is I've not been able to hide the toast notification correctly. When I make a click to show it the toast has an animation to the left the problem is when the toast is gonna hide just disapear and it does not slide to the right. Im working with react and tailwind css. Here is the code. also leave a link to the case. https://codesandbox.io/s/toast-notification-ucx2v?file=/src/components/toastNotification.jsx
I hope you can help me guys.
-----App component-----
import { useEffect, useState } from "react";
import ToastNotification from "./components/toastNotification";
import "./styles.css";
export default function App() {
const [showToast, setShowToast] = useState(false);
const addToCart = () => {
setShowToast(true);
};
useEffect(() => {
setTimeout(() => {
setShowToast(false);
}, 3000);
}, [showToast]);
return (
<div className="App">
<ToastNotification showNotification={showToast} />
<button
className="w-1/2 p-2 flex items-center justify-center rounded-md bg-indigo-400 text-white"
type="submit"
onClick={addToCart}
>
Add to cart
</button>
</div>
);
}
-----Toast component-----
import React, { useEffect, useState } from "react";
const ToastNotification = ({ showNotification }) => {
useEffect(() => {
setTimeout(() => {}, 5000);
}, []);
return (
<>
{showNotification === false ? null : (
<div className="static z-20">
<div
className={`${
showNotification
? "animate__animated animate__slideInRight absolute top-16 right-4 flex items-center text-white max-w-sm w-full bg-green-400 shadow-md rounded-lg overflow-hidden mx-auto"
: "animate__animated animate__slideOutRight absolute top-16 right-4 flex items-center text-white max-w-sm w-full bg-green-400 shadow-md rounded-lg mx-auto"
}`}
>
<div class="w-10 border-r px-2">
<i class="far fa-check-circle"></i>
</div>
<div class="flex items-center px-2 py-3">
<div class="mx-3">
<p>add to car</p>
</div>
</div>
</div>
</div>
)}
</>
);
};
export default ToastNotification;
I am trying to Make a clone of the Book My show Application. I'm building the Cast and crew slider. I tried reducing the size of the image but the spacing between the image does not seem to reduce.
This is my js file containing the src, settings for the slider:
import React from "react";
import Slider from "react-slick";
import CastPoster from "../MovieCast/movieCast.component";
const Cast = (props) => {
const settings = {
infinite: true,
autoplay: false,
speed: 1500,
slidesToShow: 4,
slidesToScroll: 2,
InitialSlide: 0,
}
const CastImages = [
{
src:"https://in.bmscdn.com/iedb/artist/images/website/poster/large/simu-liu-2006167-13-05-2021-04-13-21.jpg",
name:"Simu Liu",
role:"as Shang-Chi"
},
{
src:"https://in.bmscdn.com/iedb/artist/images/website/poster/large/awkwafina-1093500-20-06-2018-12-05-44.jpg",
name:"Awkwafina",
role:"as Katy"
},
{
src:"https://in.bmscdn.com/iedb/artist/images/website/poster/large/tony-leung-iein105711-02-04-2018-13-07-58.jpg",
name:"Tony Leung Chiu-wai",
role:"as Wenwu / The Mandarin"
},
{
src:"https://in.bmscdn.com/iedb/artist/images/website/poster/large/michelle-yeoh-1473-24-03-2017-17-32-23.jpg",
name:"Michelle Yeoh",
role:"as Jiang Nan"
}
];
return (
<>
<div className="">
<Slider {...settings}>
{
CastImages.map((data) => (
<CastPoster {...data} />
))
}
</Slider>
</div>
</>
)
};
export default Cast;
This is the js file containing the rendering of the slider:
import React from "react";
const CastPoster = (props) => {
return (
<>
<div className="">
<img className="rounded-full w-32 h-32 " src={props.src} atl={props.name} />
<div>
<h3> {props.name} </h3>
<p> {props.role} </p>
</div>
</div>
</>
);
};
export default CastPoster;
And I have added it as a component in a page:
import React from "react";
import Cast from "../components/Cast/Cast.component";
import MovieHero from "../components/MovieHero/MovieHero.component";
import offerIcon from "./offericon.png";
const MoviePage = () => {
return (
<>
<MovieHero />
<div className="my-12 container px-4 lg:w-3/4 lg:ml-20">
<div className="flex flex-col items-start gap-3">
<h2 className="text-gray-800 font-bold text-2xl"> About the movie </h2>
<p> Shang-Chi and The Legend of The Ten Rings features Simu Liu as Shang-Chi, who must confront the past he thought he left behind when he is drawn into the web of the mysterious Ten Rings organization. The film is directed by Destin Daniel Cretton and produced by Kevin Feige and Jonathan Schwartz.</p>
</div>
<div className="my-8">
<hr />
</div>
<div>
<h1 className="text-gray-800 font-bold text-2xl pb-4"> Applicable Offers </h1>
<div className="flex items-start gap-2 bg-yellow-100 border-yellow-400 border-2 border-dashed rounded-md p-3 w-96">
<img className="h-6" src={offerIcon}/>
<div className="flex flex-col items-start">
<h3 className="relative -top-1 text-gray-900 text-md font-semibold"> Filmy Pass </h3>
<p className="text-gray-600 -top-1 text-sm"> Get Rs.75* off on 3 movies you buy/rent on Stream. Buy Filmy Pass #Rs.99 </p>
</div>
</div>
</div>
<div className="my-8">
<hr />
</div>
<div>
<h3> Cast </h3>
<div>
<Cast />
</div>
</div>
</div>
</>
)
};
export default MoviePage;
The result:
This is the result of the code
I want to reduce the spacing between the images. Thank you.
The space between your images is determined by the amount of slides you want to show and the width of the slider div. To decrease the space between the images reduce the width of the slider or show more images.
Your Cast component could look like this
<div className="w-full">
<Slider {...settings} className="w-1/2">
{
CastImages.map((data) => (
<CastPoster {...data} />
))
}
</Slider>
</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).