Access JSX react variables in a button - reactjs

I have mapped a list of responses from my backend mongodb to my frontend. For each response, I have also added a button. I want to add an voteResponse function for my vote button. How can I continue to have access to my {response.respondee} and {response.response} in my voteResponse function?
{responses.map((response) => (
<div
key={response._id}
className='py-8 px-12 mb-12 bg-gray-50 border-b border-gray-100 transform transition duration-300 ease-in-out hover:-translate-y-2'
>
<NextLink href={`/responses/${response.response}`} passHref>
<div>
<div className='inline-block text-gray-900 mb-4'></div>
<h3 className='text-lg leading-normal mb-2 font-semibold text-black'>
{response.response}
</h3>
<p className='text-gray-500'>
{response.respondee.slice(0, 5)}...
{response.respondee.slice(38)}
</p>
</div>
</NextLink>
<div>
<button
type='submit'
className='inline-flex justify-center mt-3 py-1 px-3 border border-transparent shadow text-base font-medium rounded-md text-white bg-cyan-600 hover:bg-cyan-700 focus:outline-none'
onClick={voteResponse}
>
Vote
</button>
</div>
</div>
))}

You can do this by 2 ways. First is by making a separate component which is being rendered in map function and pass response in it through props then each component will have access to it's response.response and response.respondee.
funtion Response({response}){
const voteResponse = () =>{
console.log(response.response, response.respondee)
}
return(
<div key={response._id} className='py-8 px-12 mb-12 bg-gray-50 border-b border-gray-100 transform transition duration-300 ease-in-out hover:-translate-y-2'>
<NextLink href={`/responses/${response.response}`} passHref>
<div>
<div className='inline-block text-gray-900 mb-4'></div>
<h3 className='text-lg leading-normal mb-2 font-semibold text-black'>
{response.response}
</h3>
<p className='text-gray-500'>
{response.respondee.slice(0, 5)}... {response.respondee.slice(38)}
</p>
</div>
</NextLink>
<div>
<button type='submit' className='inline-flex justify-center mt-3 py-1 px-3 border border-transparent shadow text-base font-medium rounded-md text-white bg-cyan-600 hover:bg-cyan-700 focus:outline-none' onClick={voteResponse}>
Vote
</button>
</div>
</div>
)
}
//use it like this
{responses.map((response) => (
<Response response={response} key={response._id} />
))}
Second way to pass the index in voteResponse function and then through that index you can access the those variables like responses[index].response and responses[index].respondee.
{responses.map((response, index) => (
<div key={response._id} className='py-8 px-12 mb-12 bg-gray-50 border-b border-gray-100 transform transition duration-300 ease-in-out hover:-translate-y-2'>
<NextLink href={`/responses/${response.response}`} passHref>
<div>
<div className='inline-block text-gray-900 mb-4'></div>
<h3 className='text-lg leading-normal mb-2 font-semibold text-black'>
{response.response}
</h3>
<p className='text-gray-500'>
{response.respondee.slice(0, 5)}... {response.respondee.slice(38)}
</p>
</div>
</NextLink>
<div>
<button type='submit' className='inline-flex justify-center mt-3 py-1 px-3 border border-transparent shadow text-base font-medium rounded-md text-white bg-cyan-600 hover:bg-cyan-700 focus:outline-none' onClick={voteResponse}>
Vote
</button>
</div>
</div>
))}

Pass value to onClick prop: onClick={() => voteResponse(response)} and use it as parameter of voteResponse function:
const voteResponse = (res) => {
console.log("response.respondee is: ", res.respondee) //<=== response.respondee value is res.respondee value
console.log("response.response is: ", res.response)
}
{responses.map((response) => (
<div
key={response._id}
className='py-8 px-12 mb-12 bg-gray-50 border-b border-gray-100 transform transition duration-300 ease-in-out hover:-translate-y-2'
>
<NextLink href={`/responses/${response.response}`} passHref>
<div>
<div className='inline-block text-gray-900 mb-4'></div>
<h3 className='text-lg leading-normal mb-2 font-semibold text-black'>
{response.response}
</h3>
<p className='text-gray-500'>
{response.respondee.slice(0, 5)}...
{response.respondee.slice(38)}
</p>
</div>
</NextLink>
<div>
<button
type='submit'
className='inline-flex justify-center mt-3 py-1 px-3 border border-transparent shadow text-base font-medium rounded-md text-white bg-cyan-600 hover:bg-cyan-700 focus:outline-none'
onClick={() => voteResponse(response)
>
Vote
</button>
</div>
</div>
))}

Related

Jest how to test react component which partial elements are lazy load?

The code below comes from https://headlessui.com/react/popover.
As you can see, it uses a function to render Popover.Button.
In enzyme, no matter use shallow or mount, the result tree without elements wrapped by React.Fragment.
So I want to ask is there a way to render the whole dom in enzyme or #testing-library/react
export default function Example() {
return (
<div className="fixed top-16 w-full max-w-sm px-4">
<Popover className="relative">
{({ open }) => (
<>
<Popover.Button
className={`
${open ? '' : 'text-opacity-90'}
group inline-flex items-center rounded-md bg-orange-700 px-3 py-2 text-base font-medium text-white hover:text-opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75`}
>
<span>Solutions</span>
<ChevronDownIcon
className={`${open ? '' : 'text-opacity-70'}
ml-2 h-5 w-5 text-orange-300 transition duration-150 ease-in-out group-hover:text-opacity-80`}
aria-hidden="true"
/>
</Popover.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute left-1/2 z-10 mt-3 w-screen max-w-sm -translate-x-1/2 transform px-4 sm:px-0 lg:max-w-3xl">
<div className="overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5">
<div className="relative grid gap-8 bg-white p-7 lg:grid-cols-2">
{solutions.map((item) => (
<a
key={item.name}
href={item.href}
className="-m-3 flex items-center rounded-lg p-2 transition duration-150 ease-in-out hover:bg-gray-50 focus:outline-none focus-visible:ring focus-visible:ring-orange-500 focus-visible:ring-opacity-50"
>
<div className="flex h-10 w-10 shrink-0 items-center justify-center text-white sm:h-12 sm:w-12">
<item.icon aria-hidden="true" />
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-900">
{item.name}
</p>
<p className="text-sm text-gray-500">
{item.description}
</p>
</div>
</a>
))}
</div>
<div className="bg-gray-50 p-4">
<a
href="##"
className="flow-root rounded-md px-2 py-2 transition duration-150 ease-in-out hover:bg-gray-100 focus:outline-none focus-visible:ring focus-visible:ring-orange-500 focus-visible:ring-opacity-50"
>
<span className="flex items-center">
<span className="text-sm font-medium text-gray-900">
Documentation
</span>
</span>
<span className="block text-sm text-gray-500">
Start integrating products and tools
</span>
</a>
</div>
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
</div>
)
}

TypeError: items.map is not a function

My code problem is that I want to create a token from the backend and add it to my item. Now the page is crashing due to mapping the data.
<div className='bg-gray-100'>
<SimpleHeader/>
<div className='flex justify-center'>
<h2 className='text-center text-4xl py-5 inline-block border-b-4 border-blue-400 font-bold mb-4'>My Items</h2>
</div>
<Link to='/addItems' className='flex justify-center'>
<button className='px-2 py-1 bg-transparent rounded border-2 border-blue-500 hover:bg-blue-500 hover:text-white duration-200 hover:scale-105'>Add New Item</button>
</Link>
<div className='container md:grid grid-cols-3 gap-4 w-full mx-auto p-12'>
{
items?.map(item=><MyItem key={item._id} item={item} handleDelete={handleDelete}></MyItem>)
}
</div>
</div>
try to change it this way
<div className='bg-gray-100'>
<SimpleHeader/>
<div className='flex justify-center'>
<h2 className='text-center text-4xl py-5 inline-block border-b-4 border-blue-400 font-bold mb-4'>My Items</h2>
</div>
<Link to='/addItems' className='flex justify-center'>
<button className='px-2 py-1 bg-transparent rounded border-2 border-blue-500 hover:bg-blue-500 hover:text-white duration-200 hover:scale-105'>Add New Item</button>
</Link>
<div className='container md:grid grid-cols-3 gap-4 w-full mx-auto p-12'>
{
items && items.map(item=><MyItem key={item._id} item={item} handleDelete={handleDelete}></MyItem>)
}
</div>
</div>

Can't find missing key in 'Each child in a list should have a unique "key" prop'

I've been starring at my code, but I can't see where the key is missing. That is my error:
Warning: Each child in a list should have a unique "key" prop.
Check the render method of Card. It was passed a child from ContractInfo.
Here is Card:
export const Card = ({ selected, onChange, title, price, frequency, description, feature, cta }) => {
const mode = selected ? 'border-yellow-500 border-4' : 'border-gray-200'
return (
<div
className={["relative p-8 bg-white border-gray-200 rounded-2xl shadow-sm flex flex-col border-2", mode].join(' ')}
style={{cursor:'pointer'}}
selected={selected}
onClick={onChange}
>
<div className="flex-1 ">
<h3 className="text-xl font-semibold text-gray-900">{title}</h3>
<p className="mt-4 flex items-baseline text-gray-900">
<span className="text-5xl font-extrabold tracking-tight">€{price}</span>
<span className="ml-1 text-xl font-semibold">{frequency}</span>
</p>
<p className="mt-6 text-gray-500">{description}</p>
{/* Feature list */}
{feature}
</div>
<div className= 'bg-yellow-500 text-white hover:bg-yellow-600 mt-8 block w-full py-3 px-6 border border-transparent rounded-md text-center font-medium'>
{cta}
</div>
</div>
);
}
And here is the bit in ContractInfo:
return (
<div className={formStep === 0 ? 'block' : 'hidden'}>
<div className="bg-white py-16 px-4 overflow-hidden sm:px-6 lg:px-8">
<div className="relative max-w-xl mx-auto">
<div className="text-center">
<h2 className="text-3xl font-extrabold tracking-tight text-gray-600 sm:text-4xl">Welcher Vertrag passt zu dir?</h2>
</div>
</div>
<div className="max-w-7xl mx-auto py-4 px-4 bg-white 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">
{contracts.map((contract, index) => (
<Card
key={contract.id}
title={contract.title}
price={contract.price}
frequency={contract.frequency}
description={contract.description}
feature={ contract.features.map((feature, index) =>
<ul role="list" className="mt-6 space-y-6">
<li className="flex" key={feature.id} >
{console.log(feature.id)}
<CheckIcon className="flex-shrink-0 w-6 h-6 text-yellow-500" aria-hidden="true" />
{feature.name}
</li>
</ul> )}
cta={contract.cta}
selected={isSelected === index}
onChange={() => setisSelected(index)}
/>
))}
{/* {console.log(contracts?.[isSelected])} */}
</div>
</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}
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"
>
Zurück
</a>
</div>
<div className="mt-3 sm:mt-0 sm:ml-3">
<a
//onClick={nextQuizStep}
onClick={() => nextFormStep(contracts?.[isSelected]) }
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"
>
Weiter
</a>
</div>
</div>
</div>
</div>
);
}
Keys are both present for features list and for cards. Where is the problem?
Key Error is caused by feature rendering in Card component
This is cause the keys are not present among sibling elements of the for loop in feature creation, so to correct this You set key for parent ul in
feature={ contract.features.map((feature, index) =>
<ul role="list" className="mt-6 space-y-6">
<li className="flex" key={feature.id} >
{console.log(feature.id)}
<CheckIcon className="flex-shrink-0 w-6 h-6 text-yellow-500" aria-hidden="true" />
{feature.name}
</li>
</ul> )}
so it becomes
feature={ contract.features.map((feature, index) =>
<ul role="list" className="mt-6 space-y-6" key={'feature-'+feature.id}>
<li className="flex" >
{console.log(feature.id)}
<CheckIcon className="flex-shrink-0 w-6 h-6 text-yellow-500" aria-hidden="true" />
{feature.name}
</li>
</ul> )}

how can you perform an onclick function only on a specific item from an array?

I'm mapping through an array but I only want to select a specific item so I can make an onlick in it and for it to perform an specific action but I don't know how to only select that item instead of all the items in the array.
I want to only select the "Sign out" from the profile's variable. Any help I would greatly appreciate it.
import { Fragment } from 'react'
import { Disclosure, Menu, Transition } from '#headlessui/react'
import { BellIcon, MenuIcon, XIcon } from '#heroicons/react/outline'
const navigation = ['Dashboard', 'Clients', 'Meals', 'Calendar']
const profile = ['Your Profile', 'Settings', 'Sign out']
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export default function Main() {
return (
<div>
<Disclosure as="nav" className="bg-gray-800">
{({ open }) => (
<>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<div className="flex items-center">
<div className="flex-shrink-0">
<img
className="h-8 w-8"
src="https://tailwindui.com/img/logos/workflow-mark-indigo-500.svg"
alt="Workflow"
/>
</div>
<div className="hidden md:block">
<div className="ml-10 flex items-baseline space-x-4">
{navigation.map((item, itemIdx) =>
itemIdx === 0 ? (
<Fragment key={item}>
{/* Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" */}
<a href="/dashboard" className="bg-gray-900 text-white px-3 py-2 rounded-md text-sm font-medium">
{item}
</a>
</Fragment>
) : (
<a
key={item}
href={`/${item}`}
className="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium"
>
{item}
</a>
)
)}
</div>
</div>
</div>
<div className="hidden md:block">
<div className="ml-4 flex items-center md:ml-6">
<button
type="button"
className="bg-gray-800 p-1 rounded-full text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white"
>
<span className="sr-only">View notifications</span>
<BellIcon className="h-6 w-6" aria-hidden="true" />
</button>
{/* Profile dropdown */}
<Menu as="div" className="ml-3 relative">
<div>
<Menu.Button className="max-w-xs bg-gray-800 rounded-full flex items-center text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white">
<span className="sr-only">Open user menu</span>
<img
className="h-8 w-8 rounded-full"
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
alt=""
/>
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
{profile.map((item) => (
<Menu.Item key={item}>
{({ active }) => (
<a
href="#"
className={classNames(
active ? 'bg-gray-100' : '',
'block px-4 py-2 text-sm text-gray-700'
)}
>
{item}
</a>
)}
</Menu.Item>
))}
</Menu.Items>
</Transition>
</Menu>
</div>
</div>
<div className="-mr-2 flex md:hidden">
{/* Mobile menu button */}
<Disclosure.Button className="bg-gray-800 inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white">
<span className="sr-only">Open main menu</span>
{open ? (
<XIcon className="block h-6 w-6" aria-hidden="true" />
) : (
<MenuIcon className="block h-6 w-6" aria-hidden="true" />
)}
</Disclosure.Button>
</div>
</div>
</div>
<Disclosure.Panel className="md:hidden">
<div className="px-2 pt-2 pb-3 space-y-1 sm:px-3">
{navigation.map((item, itemIdx) =>
itemIdx === 0 ? (
<Fragment key={item}>
{/* Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" */}
<a href="#" className="bg-gray-900 text-white block px-3 py-2 rounded-md text-base font-medium">
{item}
</a>
</Fragment>
) : (
<a
key={item}
href="#"
className="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
>
{item}
</a>
)
)}
</div>
<div className="pt-4 pb-3 border-t border-gray-700">
<div className="flex items-center px-5">
<div className="flex-shrink-0">
<img
className="h-10 w-10 rounded-full"
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
alt=""
/>
</div>
<div className="ml-3">
<div className="text-base font-medium leading-none text-white">Tom Cook</div>
<div className="text-sm font-medium leading-none text-gray-400">tom#example.com</div>
</div>
<button
type="button"
className="ml-auto bg-gray-800 flex-shrink-0 p-1 rounded-full text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white"
>
<span className="sr-only">View notifications</span>
<BellIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
<div className="mt-3 px-2 space-y-1">
{profile.map((item) => (
<a
key={item}
href="#"
className="block px-3 py-2 rounded-md text-base font-medium text-gray-400 hover:text-white hover:bg-gray-700"
>
{item}
</a>
))}
</div>
</div>
</Disclosure.Panel>
</>
)}
</Disclosure>
</div>
)
}
I assume that you want to apply onclick to the Menu.Items
So just check for the item==='Sign out' then put your handler there.
<Menu.Items className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
{profile.map((item) => (
<Menu.Item key={item} onClick={item === "Sign out" && handleSomething()}>
{({ active }) => (
<a
href="#"
className={classNames(
active ? "bg-gray-100" : "",
"block px-4 py-2 text-sm text-gray-700"
)}
>
{item}
</a>
)}
</Menu.Item>
))}
</Menu.Items>;

Conditionally render download button in react

I have a download button from which the user can download the assets. but I want only authenticated users can download. If the user is not authenticated and click on the download button I want to show them a modal with a login button on it. I have a modal component. I am using React.
Please help me.
Getting currently authenticated user from the firebase.
const {currentUser} = useAuth();
This is my code for the download button.
<a className="bg-secondary text-white font-bold py-3
px-5 rounded text-2xl focus:outline-none mt-3 block w-48" href="YOUR_URL">
<i className="animate-bounce fas fa-arrow-down mr-2"></i>
Download
</a>
Modal Code:
import React from "react";
import { useState } from "react";
export default function Modal() {
const [showModal, setShowModal] = useState(true);
console.log('modal');
return (
<>
{showModal ? (
<>
<div
className="justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 z-50 outline-none focus:outline-none"
onClick={() => setShowModal(false)}
>
<div className="relative w-auto my-6 mx-auto max-w-3xl">
{/*content*/}
<div className="border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-white outline-none focus:outline-none">
{/*header*/}
<div className="flex items-start justify-between p-5 border-b border-solid border-gray-300 rounded-t">
<h3 className="text-3xl font-semibold">
Modal Title
</h3>
<button
className="p-1 ml-auto bg-transparent border-0 text-black opacity-5 float-right text-3xl leading-none font-semibold outline-none focus:outline-none"
onClick={() => setShowModal(false)}
>
<span className="bg-transparent text-black opacity-5 h-6 w-6 text-2xl block outline-none focus:outline-none">
×
</span>
</button>
</div>
{/*body*/}
<div className="relative p-6 flex-auto">
<p className="my-4 text-gray-600 text-lg leading-relaxed">
I always felt like I could do anything. That’s the main
thing people are controlled by! Thoughts- their perception
of themselves! They're slowed down by their perception of
themselves. If you're taught you can’t do anything, you
won’t do anything. I was taught I could do everything.
</p>
</div>
{/*footer*/}
<div className="flex items-center justify-end p-6 border-t border-solid border-gray-300 rounded-b">
<button
className="text-red-500 background-transparent font-bold uppercase px-6 py-2 text-sm outline-none focus:outline-none mr-1 mb-1"
type="button"
style={{ transition: "all .15s ease" }}
onClick={() => setShowModal(false)}
>
Close
</button>
<button
className="bg-green-500 text-white active:bg-green-600 font-bold uppercase text-sm px-6 py-3 rounded shadow hover:shadow-lg outline-none focus:outline-none mr-1 mb-1"
type="button"
style={{ transition: "all .15s ease" }}
onClick={() => setShowModal(false)}
>
Save Changes
</button>
</div>
</div>
</div>
</div>
<div className="opacity-25 fixed inset-0 z-40 bg-black"></div>
</>
) : null}
</>
);
}
You could use button instead of a and use onClick to check if currentUser exists when you click button.
This is final code:
function x {
const {currentUser} = useAuth();
const downloadAssets = () => {
if(currentUser){
window.open("url assets here", '_blank', 'noopener,noreferrer')
}else{
// Open modal here
alert("User not authenticated");
}
}
return {
<>
...
<button onClick={downloadAssets}><i className="animate-bounce fas fa-arrow-down mr-2"></I>Download</button>
...
</>
}

Resources