TailwindCSS fade in Element on click - reactjs

So I'm making this app and I need to fade in the menu when I click the button. I have it rendering on click using state, but I can't get it to fade in / fade out on click. When I edit the opacity value inside Chrome Dev Console the transition works fine, but when I want to change it using state it doesn't.
Any help? Thanks in advance!
import React, { useState } from "react";
import { useRouter } from "next/router";
import { MenuIcon, XIcon } from "#heroicons/react/outline";
function Header() {
const router = useRouter();
const [popCard, setPopCard] = useState("hidden");
const [fade, setFade] = useState(true);
const handleMenuClick = () => {
setPopCard("inline-block");
setFade(true);
};
const handleXClick = () => {
setPopCard("hidden");
setFade(false);
};
return (
<div className="text-center">
<header className="sticky z-50 top-0 shadow-md bg-white border-b p-5">
<div className="flex justify-between items-center">
<h1
className="text-6xl text-red-500 cursor-pointer"
onClick={() => router.push("/")}
>
Velvet
</h1>
<MenuIcon
className="h-8 text-red-500 cursor-pointer"
onClick={handleMenuClick}
/>
</div>
</header>
<div
className={
popCard +
" w-[60%] flex-col border my-10 pb-3 rounded-3xl shadow-lg transition duration-300 ease-in-out " +
`${fade === true ? "opacity-100" : "opacity-0"}`
}
>
<div className="flex justify-end">
<XIcon
className="h-6 text-red-500 cursor-pointer mt-2 mr-2 opacity-70"
onClick={handleXClick}
/>
</div>
<div className="space-y-8 text-3xl text-center mt-5 mb-10 text-red-500">
<h1>Contac</h1>
<h1>About Us</h1>
</div>
</div>
</div>
);
}
export default Header;
codesandbox: Sandbox
Just to be clear, I want the menu card to fade in when I click the menu button, and I want the menu card to fade out when I click the close button.

The solution is, you need to add duration, like this:
`transition-all duration-200 ${fade ? "opacity-100" : "opacity-0"}`
Here is my forked sandbox you had given, I've removed extra inline CSS, so it may become evident.
Here is the complete code:
function Header() {
const [popCard, setPopCard] = useState("hidden");
const [fade, setFade] = useState(false);
const handleMenuClick = () => {
setPopCard("inline-block");
setFade(true);
};
const handleXClick = () => {
setPopCard("hidden");
setFade(false);
};
console.log(fade, "fade");
return (
<div className="text-center">
<header className="sticky z-50 top-0 shadow-md bg-white border-b p-5">
<div className="flex justify-between items-center">
<h1 className="text-6xl text-red-500 cursor-pointer">Velvet</h1>
<button
className="text-3xl border rounded-lg px-5"
onClick={handleMenuClick}
>
Menu
</button>
</div>
</header>
<div className="p-10">
<div
className={`transition-all duration-200 ${
fade ? "opacity-100" : "opacity-0"
}`}
>
<div className="flex justify-end">
<button className="mt-2 mr-2 border p-2" onClick={handleXClick}>
Close
</button>
</div>
<div className="space-y-2 text-3xl text-center mt-5 mb-10 mx-5 text-red-500">
<h1>Kontakt</h1>
<h1>O Velvetu</h1>
</div>
</div>
</div>
</div>
);
}
export default Header;
Sandbox: https://codesandbox.io/s/sweet-swartz-mr3nru?file=/pages/index.js:41-1396

Related

How to Show or Hide a div using useState in NextJS?

I’m new to React but trying to build a responsive website with a “desktop header” and “mobile header” that is shown when the user clicks on a menu-icon-toggle and closes when the user clicks on Close-icon.
I’m obviously doing it wrong but can’t seem to figure out what the problem is, I believe that NextJS doesn’t know what to open or close.
**Note: I´m using TailwindCSS and this is a component that will be rendered on the index page
My code looks something like this (simplified, without all the content):
import React, { useState } from 'react'
import Image from 'next/Image'
function header() {
const \[mobile__Header, setMobile__Header\] = useState(false)
const showMobile__Header = () =\> setMobile__Header(!mobile__Header)
return (\<div\>
{/* mobile header */}
<div className='absolute flex flex-col w-screen h-screen place-content-between bg-white text-black p-5 z-50'>
<div className='flex items-center justify-between'>
{/* Left Logo */}
<div className='cursor-pointer'>
</div>
{/* close icon */}
<div className='cursor-pointer' onClick={showMobile__Header}>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
</div>
{/* nav links */}
<div className='flex'>
<div className='flex flex-col text-xl space-y-3'>
</div>
</div>
{/* Social links and languaje changer */}
<div className='flex justify-between font-light'>
<div>
<a className="link" href="">EN</a>
</div>
<div className='flex flex-col'>
</div>
</div>
</div>
{/* desktop header */}
<header className="flex w- px-10 py-1 justify-between">
<div className="flex">
{/* Left Logos */}
<div className="flex md:hidden cursor-pointer">
</div>
<div className="hidden md:flex cursor-pointer">
</div>
</div>
<div className="flex items-center">
{/* Menu icon toggle */}
<div className='flex md:hidden cursor-pointer' onClick={showMobile__Header}>
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</div>
{/* Right Nav Links and language changer */}
<div className="space-x-4 px-5 py-5 hidden md:flex ">
</div>
</div>
</header>
</div>
)}
export default header
You have many HTML/CSS issues like positioning and element structure.
The free tailwindui example is a solid example to reference. It has nice transitions and accessibility baked in, which I removed for the example. It also uses headlessui and heroicons, both were built by the TW team. The TW menu components handle the state internally, so you will not be able to see the logic in their example.
The below responsive example is based on the above-referenced version but without external dependencies.
import { useState } from "react";
const Navbar = () => {
const [isOpen, setOpen] = useState(false);
const toggleMenu = () => setOpen(!isOpen);
return (
<header className="relative bg-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6">
<div className="flex justify-between items-center border-b-2 border-gray-100 py-6 md:justify-start md:space-x-10">
<div className="flex justify-start lg:w-0 lg:flex-1">
<a href="#">
<span className="h-8 w-auto sm:h-10">LOGO</span>
</a>
</div>
<div className="-mr-2 -my-2 md:hidden">
<button
onClick={toggleMenu}
className="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500"
>
Open
</button>
</div>
<nav className="hidden md:flex space-x-10">
<a href="#" className="text-base font-medium text-gray-500 hover:text-gray-900">
About
</a>
</nav>
</div>
</div>
{isOpen && (
<div className="absolute top-0 inset-x-0 p-2 transition transform origin-top-right md:hidden">
<div className="rounded-lg shadow-lg ring-1 ring-black ring-opacity-5 bg-white divide-y-2 divide-gray-50">
<div className="pt-5 pb-6 px-5">
<div className="flex items-center justify-between">
<div>
<span className="h-8 w-auto">LOGO</span>
</div>
<div className="-mr-2">
<button
onClick={toggleMenu}
className="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500"
>
X
</button>
</div>
</div>
<div className="mt-6">
<nav className="grid gap-y-8">
<a href="#" className="p-3 flex items-center rounded-md hover:bg-gray-50">
About
</a>
</nav>
</div>
</div>
</div>
</div>
)}
</header>
);
};
You will also likely need to handle the closing of the menu on route change.
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
const Navbar = () => {
const [isOpen, setOpen] = useState(false);
const toggleMenu = () => setOpen(!isOpen);
const router = useRouter();
useEffect(() => {
const closeMenu = () => isOpen && setOpen(false);
router.events.on("routeChangeStart", closeMenu);
return () => {
router.events.off("routeChangeStart", closeMenu);
};
}, [isOpen, router]);
return (
...see above example
Without knowing exactly what you are asking, this should set you down the right path, at least from a logic standpoint.
import React, { useState } from 'react'
import Image from 'next/Image'
function header() {
const [mobile__Header, setMobile__Header] = useState(false)
const showMobile__Header = (e) => {
if (e.target.className.includes('mobile')) {
setMobile__Header(true)
} else if (e.target.className.includes('desktop')){
setMobile__Header(false)
}
}
return (
<div>
<div className='absolute flex flex-col w-screen h-screen place-content-between bg-white text-black p-5 z-50'>
<div className='flex items-center justify-between'>
<div className='cursor-pointer'>
</div>
<div className={mobile__Header === true ? 'cursor-pointer-mobile' : 'remove-display'} onClick={showMobile__Header}>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
</div>
<div className='flex'>
<div className='flex flex-col text-xl space-y-3'>
</div>
</div>
<div className='flex justify-between font-light'>
<div>
<a className="link" href="">EN</a>
</div>
<div className='flex flex-col'>
</div>
</div>
</div>
<header className="flex w- px-10 py-1 justify-between">
<div className="flex">
<div className="flex md:hidden cursor-pointer">
</div>
<div className="hidden md:flex cursor-pointer">
</div>
</div>
<div className="flex items-center">
<div className={mobile__Header === false ? 'cursor-pointer-desktop' : 'remove-display'} onClick={showMobile__Header}>
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</div>
<div className="space-x-4 px-5 py-5 hidden md:flex ">
</div>
</div>
</header>
</div>
)
}
export default header
Basically, make sure to differentiate between your mobile button and your desktop button using your classNames. Then, you set a bolean indicator depending on what is in the className. From there, you either display the correct container, or use a CSS class that simply puts display: none (in this case, I used the name remove-display. All that is done with an inline ternary operator.
Whether you are toggling a button, or toggling a whole container (a parent div that when display: none removes all the content inside), this approach works in both scenarios.
I am still confused as to what you are asking but my solution should get you going. You have an onClick function on two divs and the comments above each of those says are misleading. One says close icon and the other says menu icon toggle. I am not sure what we are toggling.
EDIT: Here is a much cleaner way to do it and it involved no className toggling. This will teach you what you are doing so you can actually toggle what you are trying to toggle:
import React, { useState } from 'react'
function header() {
const [isActivate, setActive] = useState(false)
const handleActivate = (e) => {
!isActivate ? setActivate(true) : setActivate(false)
}
return (
<div>
<div>
<h1 style={{cursor: 'pointer'}} onClick={handleActivate}>
{isActivate === true ? 'OPEN' : false}
</h1>
<h1 style={{cursor: 'pointer'}} onClick={handleActivate}>
{!isActivate ? 'CLOSE' : false}
</h1>
</div>
</div>
)
}
export default header

React tailwind css -right position show scroll bar

I have sticky box with show hide button as shown in image. It is working but if I tried to hide there is horizontal scroll bar and can see the box as shown in image 2. To colapse the box, I change right-0 to -right-24. Is there anyway not to show the horizontal scroll bar.
Image 1 -: showing sticky bar and can click setting icon to hide the bar. There is no horizontal scroll bar.
Image 2 -: after hide the bar, horizontal scroll bar is appeared and can see the box when I scroll.
const TrackPage = () => {
const handleMenuCollapse = (e) => {
e.preventDefault();
setColapse(!colapse);
};
return (
<>
<div>
<div
className={`flex w-24 h-96 absolute top-[calc((100vh-384px)/2)] ${
colapse ? "-right-24" : "right-0"
} " bg-primary rounded-b-lg items-center justify-center`}
>
<div
className="flex w-12 h-12 absolute -top-0 -left-12 cursor-pointer bg-primary rounded-l-lg items-center justify-center"
onClick={handleMenuCollapse}
>
<SettingIcon />
</div>
Setting
</div>
</div>
</>
);
};
export default TrackPage;
App.jsx
import React, { useState, useEffect } from "react";
import Header from "./components/header";
import SideMenu from "./components/side_menu";
import AppRoutes from "./routes";
import withUser from "./hocs/with_user";
import { isMobile } from "react-device-detect";
import { useLocation } from "react-router-dom";
import { AuthProvider, setAccessToken } from
"./auth/auth_provider";
import { Toaster } from "react-hot-toast";
import AppContext from "./components/app_context";
import "./i18n";
import "./App.css";
function App(props) {
const [colapse, setColapse] = useState(isMobile);
const [sideBarFull] = useState(true);
const location = useLocation();
const IsNormalPage = () => {
const blankPages = ["/login"];
for (let i = 0; i < blankPages.length; i++) {
if (location.pathname.startsWith(blankPages[i])) return
false;
}
return true;
};
useEffect(() => {
if (props.user) setAccessToken(props.user.t);
}, []);
const PageHeader = () => {
return (
<div className="h-[72px] w-full flex items-center align-middle justify-center bg-neutral shadow">
<div className="w-full text-center">
<Header />
</div>
</div>
);
};
return (
<AuthProvider user={props.user}>
<AppContext.Provider
value={{
colapse: colapse,
setColapse: setColapse,
}}
>
<div className="relative w-full min-h-screen h-full">
<div className="flex flex-row min-h-screen">
<div className="w-auto z-0 ">
<div className="flex-1 w-full max-h-screen mx-auto text-lg h-full shadow-lg bg-white overflow-y-auto">
{IsNormalPage() && <SideMenu showFullMenu={sideBarFull} />}
</div>
</div>
<div className="w-full max-h-screen flex flex-col z-10">
{IsNormalPage() && <PageHeader />}
<div className="flex-1 w-full max-h-screen mx-auto text-lg h-full shadow-lg bg-white overflow-y-auto">
<Toaster />
<AppRoutes />
</div>
</div>
</div>
</div>
</AppContext.Provider>
</AuthProvider>
);
}
export default withUser(App);
I figured out a solution for this
First let's divide this section into two sections (the settings icon and the bigger div that you want to hide)
Then add this to the big div className: ${colapse ? "hidden" : "right-0"} so it will just be hidden instead of -right-24
and for the icon div add this to its className:${colapse ? "animate-pulse right-0": "right-24"} the animation is of course optional I just added it.
This is the code but I forgot and named the component Side.jsx instead of TrackPage.jsx
import React, { useState } from "react";
function Side() {
const [colapse, setColapse] = useState(true);
const handleMenuCollapse = (e) => {
e.preventDefault();
setColapse(!colapse);
};
return (
<div>
{/** the big div */}
<div
className={`flex w-24 h-96 bg-indigo-500 absolute top-[calc((100vh-384px)/2)] ${
colapse ? "hidden" : "right-0"
} " bg-primary rounded-b-lg items-center justify-center`}
>
Setting
</div>
{/** the icon div */}
<div
className={`flex w-12 absolute top-[calc((100vh-384px)/2)] h-12 bg-indigo-500
${colapse ? "animate-pulse right-0" : "right-24"}
cursor-pointer bg-primary rounded-l-lg items-center justify-center`}
onClick={handleMenuCollapse}
>
<div className="w-5 h-5 bg-white rounded-full " />
</div>
</div>
);
}
export default Side;
take a look Live on codesandbox.

React Stepper change Status onClick

I am new to Ract and building a multi step form in Next.js, where I also use Context. So my project structure is pretty wild and I don't get how / where to change the steps.status when moving to next step in the stepper. So far I have my context, managing half of states, basically the formData, but also the 'original' state of my stepper:
import { useState, createContext, useContext } from "react";
export const FormContext = createContext();
export default function FormProvider({ children }) {
const [data, setData] = useState({});
const [steps, setSteps] = useState([
{ name: 'Vertrag', status: 'current' },
{ name: 'Dateneingabe', status: 'upcoming' },
{ name: 'Bestätigung', status: 'upcoming' },
]);
const setFormValues = (values) => {
setData((prevValues) => ({
...prevValues,
...values,
}));
};
return (
<FormContext.Provider value={{ steps, data, setFormValues }}>
{children}
</FormContext.Provider>
);
}
export const useFormData = () => useContext(FormContext);
In Stepper.js I therefore import my formData:
import { CheckIcon } from '#heroicons/react/solid'
import { useContext } from 'react'
import { FormContext } from "../context";
// status: 'complete', 'current', 'upcoming'
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export default function Stepper() {
const formData = useContext(FormContext);
const steps = formData.steps
return (
<nav aria-label="Progress">
<div className="flex items-center flex-col">
<ol className="flex items-center sm:flex-col md:flex-row mx-auto mt-32 mb-8">
{steps.map((step, stepIdx) => (
<li key={step.name} className={classNames(stepIdx !== steps.length - 1 ? 'pr-16 sm:pr-32' : '', 'relative')}>
{step.status === 'complete' ? (
<>
<div className="absolute inset-0 flex items-center" aria-hidden="true">
<div className="h-0.5 w-full bg-yellow-500" />
</div>
<a
href="#"
className="relative w-8 h-8 flex items-center justify-center bg-yellow-500 rounded-full hover:bg-yellow-500"
>
<span className="h-9 flex flex-col items-center">
<span className="relative top-2 z-10 w-8 h-8 flex items-center justify-center rounded-full group-hover:bg-indigo-800">
<CheckIcon className="w-5 h-5 text-white" aria-hidden="true" />
</span>
<span className="text-xs font-semibold tracking-wide text-gray-600 mt-8">{step.name}</span>
</span>
</a>
</>
) : step.status === 'current' ? (
<>
<div className="absolute inset-0 flex items-center" aria-hidden="true">
<div className="h-0.5 w-full bg-gray-200" />
</div>
<a
href="#"
className="relative w-8 h-8 flex items-center justify-center bg-white border-2 border-yellow-500 rounded-full"
aria-current="step"
>
<span className="h-9 flex flex-col items-center">
<span className="z-10 w-8 h-8 flex items-center justify-center rounded-full group-hover:bg-indigo-800">
<span className="relative h-2.5 w-2.5 bg-yellow-500 rounded-full relative" style={{top: '0.8rem'}} />
</span>
<span className="text-xs font-semibold tracking-wide text-gray-600" style={{marginTop: '2.72rem'}}>{step.name}</span>
</span>
</a>
</>
) : (
<>
<div className="absolute inset-0 flex items-center" aria-hidden="true">
<div className="h-0.5 w-full bg-gray-200" />
</div>
<a
href="#"
className="group relative w-8 h-8 flex items-center justify-center bg-white border-2 border-gray-300 rounded-full hover:border-gray-400"
>
<span className="h-9 flex flex-col items-center">
<span className="z-10 w-8 h-8 flex items-center justify-center rounded-full">
<span className="relative h-2.5 w-2.5 bg-transparent rounded-full group-hover:bg-gray-300" style={{top: '0.8rem'}} />
</span>
<span className="text-xs font-semibold tracking-wide text-gray-600" style={{marginTop: '2.72rem'}}>{step.name}</span>
</span>
</a>
</>
)}
</li>
))}
</ol>
</div>
</nav>
)
Moreover I have index.js page, where all the components come together
import { useState } from "react";
import Head from "next/head";
import Stepper from '../components/Stepper'
import styles from "../styles/styles.module.scss";
import FormCard from "../components/FormCard";
import Navbar from "../components/Navbar";
import { CheckIcon } from '#heroicons/react/solid'
import {
PersonalInfo,
ConfirmPurchase,
ContractInfo,
} from "../components/Forms";
import FormCompleted from "../components/FormCompleted";
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
const App = () => {
const [formStep, setFormStep] = useState(0);
const nextFormStep = () => setFormStep((currentStep) => currentStep + 1);
const prevFormStep = () => setFormStep((currentStep) => currentStep - 1);
const [activeStep, setActiveStep] = useState(0);
const handleNextStep = () => {
setActiveStep((prevActiveStep) => prevActiveStep + 1);
};
const handlePrevoiusStep = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
return (
<div>
<Head>
<title>Next.js Multi Step Form</title>
</Head>
< Navbar />
< Stepper activeStep={activeStep} />
<div className={styles.container}>
<FormCard currentStep={formStep} prevFormStep={prevFormStep}>
{formStep >= 0 && (
<ContractInfo formStep={formStep} nextFormStep={nextFormStep} />
)}
{formStep >= 1 && (
<PersonalInfo formStep={formStep} nextFormStep={nextFormStep} />
)}
{formStep >= 2 && (
<ConfirmPurchase formStep={formStep} nextFormStep={nextFormStep} />
)}
{formStep > 2 && <FormCompleted />}
</FormCard>
</div>
<div className="mt-1 mb-5 sm:mt-8 sm:flex sm:justify-center lg:justify-center">
<div className="rounded-md shadow">
<a role="button" tabIndex={0}
onClick={ () => { prevFormStep(); handlePrevoiusStep() }}
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-yellow-500 hover:bg-yallow-600 md:py-4 md:text-lg md:px-10"
>
Back
</a>
</div>
<div className="mt-3 sm:mt-0 sm:ml-3">
<a
onClick={ () => { nextFormStep(); handleNextStep() }}
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-yellow-500 hover:bg-yallow-600 md:py-4 md:text-lg md:px-10"
>
Next
</a>
</div>
</div>
</div>
);
};
export default App;
As you see, Stepper is managed in three different files. But I am at least capable to change the activeStep index when clicking on Buttons, which already was a huge challenge for me. So now I also need the design to change. So that activeStep gets the step.status === 'current'. All stepps index < activeStep index should get step.status === 'complete' and logically all stepps index > activeStep index - step.status === 'upcoming'. Now I tried to handle this in index.js, but of course get back step is undefined, even though it is defined in Stepper.js through context.

Pass selected component to next step of form react

I have a multi step form, which is a quiz. I already set up the context to be able to pass data.
import { useState, createContext, useContext } from "react";
export const QuizContext = createContext();
export default function QuizProvider({ children }) {
const [data, setData] = useState({});
const setQuizValues = (values) => {
setData((prevValues) => ({
...prevValues,
...values,
}));
};
return (
<QuizContext.Provider value={{ data, setQuizValues }}>
{children}
</QuizContext.Provider>
);
}
export const useQuizData = () => useContext(QuizContext);
So, my first step is selecting a card.
The code below:
import { Card } from "../../stories/Card";
import { useQuizData } from "../../context/index"
import { useState } from "react";
const tacos = [
{
cathegory: 'Meat',
imgURL: 'https://images.unsplash.com/photo-1560781290-7dc94c0f8f4f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3024&q=80'
},
{
cathegory: 'Fish',
imgURL: 'https://images.unsplash.com/photo-1510130387422-82bed34b37e9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=975&q=80'
},
{
cathegory: 'Veggi',
imgURL: 'https://images.unsplash.com/photo-1572527129705-a6c197003d61?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=975&q=80'
},
]
export const TacoCathegories = ({quizStep, prevQuizStep, nextQuizStep}) => {
// const { setQuizValues } = useQuizData();
const [isSelected, setisSelected] = useState();
const handleSubmit = (values) => {
setQuizValues(values);
prevQuizStep();
nextQuizStep();
};
return (
<div className={quizStep === 0 ? 'block': 'hidden'}>
<div className="text-center">
<h2 className="text-3xl font-extrabold tracking-tight text-gray-600 sm:text-4xl">What is your favourite taco group?</h2>
</div>
<div className="max-w-7xl mx-auto py-24 px-4 sm:px-6 lg:px-8">
<div className="mt space-y-12 lg:space-y-0 lg:grid lg:grid-cols-3 lg:gap-x-8">
{tacos.map((taco, index) => (
<Card
role="button"
key={index}
title={taco.cathegory}
source={taco.imgURL}
text={`image of ${taco.cathegory} `}
selected={isSelected === index}
onChange={() => setisSelected(index)}
/>
))}
</div>
{tacos[isSelected] && <p>{tacos[isSelected].cathegory}</p>}
<div className="mt-5 sm:mt-8 sm:flex sm:justify-center lg:justify-center">
<div className="rounded-md shadow">
<a role="button" tabIndex={0}
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-gray-200 hover:bg-gray-200 focus:outline-none md:py-4 md:text-lg md:px-10 cursor-not-allowed"
>
Back
</a>
</div>
<div className="mt-3 sm:mt-0 sm:ml-3">
<a
onClick={nextQuizStep}
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-yellow-500 hover:bg-yallow-600 md:py-4 md:text-lg md:px-10"
>
Next
</a>
</div>
</div>
</div>
</div>
);
}
I am able to get the category of selected card tacos[isSelected].cathegory. Depending on the category I need to render different content in Step 2 of my multistep form. Basically, if I choose Meat I will render cards with Meat Tacos, If I choose Fish - with Fish Tacos. For now second step is empty, because I couldn't figure out how to pass selected category to second step.
export const TacoTypes = ({quizStep, prevQuizStep, nextQuizStep}) => {
return (
<div className={quizStep === 1 ? 'block' : 'hidden'}>
<div>
<p>Taco Types are: all you find in the object</p>
</div>
<div className="mt-5 sm:mt-8 sm:flex sm:justify-center lg:justify-center">
<div className="rounded-md shadow">
<a role="button" tabIndex={0}
onClick={prevQuizStep}
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-yellow-500 hover:bg-yallow-600 md:py-4 md:text-lg md:px-10"
>
Back
</a>
</div>
<div className="mt-3 sm:mt-0 sm:ml-3">
<a
onClick={nextQuizStep}
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-yellow-500 hover:bg-yallow-600 md:py-4 md:text-lg md:px-10"
>
Next
</a>
</div>
</div>
</div>
)
}
I am new to react, so any tipp would be helpful!
Sandbox: https://codesandbox.io/s/agitated-euler-pny5n

Post.js is not showing the blog post...Are there any errors here?

There should be a block of blog post here but its empty:
Please check the map and the other syntaxes, I am not getting it.
import React,{useState,useEffect} from "react";
import { Link } from "react-router-dom";
import sanityClient from "../client.js";
This is the export default function below
export default function Post(){
const [postData, setPost] = useState(null);
useEffect(() => {
sanityClient
.fetch(`*[_type =="post"]{
title,
slug,
mainImage{
asset->{
_id;
url
},
alt
}
}`)
.then((data) => setPost(data))
.catch(console.error);
},[]);
I think there is something missing in postData map.
return (
<main className="bg-green-100 min-h-screen p-12">
<section className="container mx-auto">
<h1 className="text-5xl flex justify-center cursive">Blog Posts Page</h1>
<h2 className="text-lg text-gray-600 flex justify-center mb-12">Welcome to my world!</h2>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{postData && postData.map((post,index) => (
<article>
<Link to={"/post/" + post.slug.current} key={post.slug.current}>
<span className="block h-64 relative rounded shadow leading-snug bg-white border-l-8 border-green-400" key={index}>
<img src={post.mainImage.asset.url} alt={post.mainImage.alt} className="w-full h-full rounded-r object-cover absolute" />
<span className="block relative h-full flex justify-end items-end pr-4 pb-4">
<h3 className="text-gray-800 text-lg font-blog px-3 py-4 bg-red-700 text-red-100 bg-opacity-75 rounded">{post.title}</h3>
</span>
</span>
</Link>
</article>
))}
</div>
</section>
</main>
)
}

Resources