How to make react-collapsed (useCollapse) works into a map? - javascript

I have implemented the react-collapsed library to make a collapse button works. I need to implement this button inside a loop, but I didn't find a way to make it works properly inside a loop. If I press the 1 button to expand 1 section, it expands every section in the loop, so it's not working well.
import React from "react";
import AllIcons from "../../ui/icons/all-icons";
import TableSellDataCard from "./TableSellDataCard";
import useCollapse from 'react-collapsed';
const TableContent = ({ data, columns, }) => {
const { getCollapseProps, getToggleProps, isExpanded } = useCollapse();
return (
<>
{data.map((item, index) => {
return (
<div key={index}>
<div className="flex px-5 border-b border-gray-form py-2" data-reservation-id={item.id}>
{columns.map(() => {
return (
<div>
<button className="accordion" {...getToggleProps()}>
{isExpanded ? <AllIcons name="AccordeonArrow" /> : <AllIcons name="AccordeonArrowUp" />}
</button>
</div>
);
})}
</div>
<section {...getCollapseProps()}><TableSellDataCard /></section>
</ div>
);
})}
</>
);
};
export default TableContent;
I have one button that toggles the section, but it expands all the sections inside the loop. I need to make it work. Button 1 only opens section 1, and button 2 only opens section 2.

Related

How to preserve state of flip card after navigating to another page and then coming back to same page

I tried to preserve the state of flip card, when I flip card I navigated to another route and after coming back from that page the card again come back to its original state, I want to preserve the flipped card state(back side).here is the code first component that renders is Cards and second one is FlippableCard and third one is Cards.
const Cards = () => {
return (
<>
<Navbar></Navbar>
<div className="round-box">Flip a Card</div>
<div className="flex-container">
<Remaincard />
<div className="flex-container-child">
<div className="flex-child">
<FlippableCard title={data[0].cardName} key={0} />
</div>
</div>
</div>
</>
);
};
export default Cards;
function Card({ onClick, title }) {
const navigate = useNavigate();
const timeOutFun = (e) => {
setTimeout(() => navigate("../afterflip/" + title), 300);
console.log(title);
};
return (
<>
<div className="card" onClick={onClick}>
<div className="card-back"></div>
<div className="card-front">
<button
className="middle card-front"
onClick={() => {
timeOutFun();
}}
>
hiii
</button>
<p
onClick={() => {
timeOutFun();
}}
className="text-on-card"
>
{title}
</p>
</div>
</div>
</>
);
}
function FlippableCard({ title, key }) {
const [showFront, setShowFront] = useState(true);
// console.log("showFront");
const [color, setColor] = useState("#110781");
return (
<div className="scroll-remove">
<div className="flippable-card-container">
<CSSTransition in={showFront} timeout={300} classNames="flip">
<Card
title={title}
value={key}
onClick={() => {
setShowFront((v) => !v);
setColor("#A8A8A8");
setShowFront(false);
// getLocalData();
}}
/>
</CSSTransition>
</div>
</div>
);
}
You might want to look into using localStorage.setItem and localStorage.getItem. This will store the data in the browser indefinitely using cache. If you want the data to be deleted or in other words, refreshed, you can use session storage. This retains data until that particular tab is closed.

Change styles on hover of an appropriate item

How can I Change styles on hover of an appropriate item react? Now the styles are changed of all of the items at a time. Hovering on add to cart button I need to change only the chosen div styles.
https://codesandbox.io/s/trusting-moon-djocul?file=/src/components/Category.js**
import React, { useState } from "react";
import styles from "./category.module.css";
import Categories from "./Categories";
const Category = () => {
const [hovered, setHovered] = useState(false);
const [data, setData] = useState(Categories);
const style = hovered
? { backgroundColor: "#ffcbcb", color: "#fff", transition: "all 0.5s ease" }
: {};
const filterResult = (catItem) => {
const result = Categories.filter((curData) => curData.category === catItem);
setData(result);
};
return (
<>
<div className={styles.items}>
{data.map((value) => {
const { id, title, price, description } = value;
return (
<>
<div className={styles.item} key={id} style={style}>
<h1>{title}</h1>
<p>
{price} <span>$</span>
</p>
<p>{description}</p>
<button
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
className={styles.btn}
>
Add to Cart
</button>
</div>
</>
);
};
export default Category;
That's because you have a single "hovered" state shared between all your cards, you should create a "Card" component and have that component have its own hovered state
return (
<>
<div className={styles.items}>
{data.map((value) => {
return (
<>
<Card {...props} />
</>
);
Problem
This issue is occurring cause you are applying CSS to all the cards. The only small thing you are missing in your logic is adding CSS to the only card whose button was being hovered.
Solution
That can be achieved by using event objects on mouse enter and mouse leave events.
<div className={styles.item} key={id} style={style}>
<h1>{title}</h1>
<p>
{price} <span>$</span>
</p>
<p>{description}</p>
<button
onMouseEnter={(e) =>
e.currentTarget.parentElement.classList.add("active_card")
}
onMouseLeave={(e) =>
e.currentTarget.parentElement.classList.remove("active_card")
}
className={styles.btn}
>
Add to Cart
</button>
</div>
This will add a class of "active_card" on the card whose Add To Card button is being hovered, then you can apply your desired styling to that class.
.active_card {
background-color: #ffcbcb !important;
}
Example
Working Code Sandbox Example

In react, how to pass states down as props?

This is a very noob question but I've been trying all day do implement this. Please help me out.
Sorry for the length, just tried to put out the whole thing I am struggling with
I am trying to build custom buttons and to do so, I created a component so I can create as many buttons that I want. For that I declared a state and passed down some information as props, which is as follows:
import React, {useState} from 'react'
import Button from '../components/Button'
function CustomButton() {
const [clicked, setClicked] = useState(false)
return (
<div className='CustomButton'>
<Navbar />
<Button setClicked={setClicked} name="Button One" clicked={clicked}/>
<Button setClicked={setClicked} name="Button Two" clicked={clicked}/>
<Button setClicked={setClicked} name="Button Three" clicked={clicked}/>
</div>
)
}
export default CustomButton
As you can see, we passed the state and name of that button down. To render this Buttons, following component has been created:
import React from 'react'
import Modal from './Modal/Modal'
function Button({setClicked, name, clicked}) {
return (
<div>
<button onClick={() => {setClicked(true)}}>{name}</button>
{clicked && <Modal closeModal={setClicked} name={`You Clicked ${name}`} />}
</div>
)
}
export default Button
And lastly, when once a button is clicked, we want to perform some action. That action is to pop the Modal on a screen. And to do so, we created a Modal and passed down few props. Code for the same is as follows:
import React from 'react'
function Modal({closeModal, name}) {
return (
<div className='modal'>
<div className='modalContainer'>
<p>{name}</p>
<div>
<button onClick={() => {closeModal(false)}}>×</button>
</div>
</div>
</div>
)
}
export default Modal
The expected result is for a Modal to pop with "You clicked button One", supposing we clicked one something similar to this.
The actual result is that all three Modals pop up one above the other when any of the three buttons are passed. The result:
I realize that I am passing the states wrong way. When any of the button is clicked all three get set to true. I simply don't realize how. Don't they create a method for each one?
Also, can you guys please teach me a better/understandable way to write clicked logic. Like maybe
if(clicked){
<Modal closeModal={setClicked} name={`You Clicked ${name}`} />
}
Because you bind all three buttons with one state, You need a state as array, with items equal to the number of buttons.
const [clicked, setClicked] = useState([false, false, false])
return (
<div className='CustomButton'>
<Navbar />
{
clicked.map((button, i) => {
return <Button setClicked={setClicked} name="Button Three" clicked={clicked[i]} index={i}/>
})
}
</div>
)
Then in the button component.
function Button({setClicked, name, clicked, index}) {
return (
<div>
<button onClick={() => {setClicked(prev => prev.map((item, i) => {
return i === index ? true : item
}))}}>{name}</button>
{clicked && <Modal closeModal={setClicked} name={`You Clicked ${name}`} />}
</div>
)
}
And the modal component.
function Modal({ closeModal, name, index }) {
return (
<div className="modal">
<div className="modalContainer">
<p>{name}</p>
<div>
<button
onClick={() => {
closeModal((prev) =>
prev.map((item, i) => {
return i === index ? false : item;
})
);
}}
>
×
</button>
</div>
</div>
</div>
);
}
You can find a working example on this link.
https://codesandbox.io/s/old-wood-zgjno9
You can implement multiple modals like this:
import { useState } from "react";
export default function App() {
const [showModal1, setShowModal1] = useState(false);
const [showModal2, setShowModal2] = useState(false);
return (
<div className="App">
<button onClick={(e) => setShowModal1(true)}>Button 1</button>
<button onClick={(e) => setShowModal2(true)}>Button 2</button>
{showModal1 && (
<Modal text="Modal 1" onClose={(e) => setShowModal1(false)} />
)}
{showModal2 && (
<Modal text="Modal 2" onClose={(e) => setShowModal2(false)} />
)}
</div>
);
}
const Modal = ({ text, onClose }) => {
return (
<div>
{text}
<button onClick={onClose}>Close</button>
</div>
);
};
Working example

React and Sanity - Button is targeting wrong data

I am building my website portfolio using React and Sanity. This actually is my first project with React. The idea was to use sanity in order to store data that I can use on my website, such as "projects" and so far everything is going well, except for one thing: THE BUTTON IS TARGETING WRONG DATA.
The projects are divided in categories: UX/UI - React - JavaScript - University Projects - All
Everything is working fine, the tags imported from sanity's schemas allow me to categorise the projects.
Every project looks like a little card and when hovered, there is a little description as long as the button "MORE+".
HERE IS THE PROBLEM
When I click the button, there is a big window showing up where I can see what is the project about.
Right now there are two projects on sanity (let's call them A and B).
Project A is categorised as JavaScript and project B as React and UI/UX.
If I hover on project A and Click the button "MORE+", it would open project B on the big window, why is that?
This happens only when I am in the category "ALL" but I assume it doesn't happen in other categories only because there is only one project each category, while in "ALL" both projects are shown.
I leave below the code that I used for the button and how I imported this from sanity.
It may look a bit confusing and long, only because I used a lot of motion frame and wrapped everything in a lot of div
Also in few point it is still uncomplete.
import React, { useState, useEffect } from 'react';
import {AiFillEye, AiFillGithub} from 'react-icons/ai';
import {motion} from 'framer-motion';
import './Work.scss';
import { HiX } from 'react-icons/hi';
import { AppWrap } from '../../wrapper';
import {urlFor, client} from '../../client';
const Work = () => {
const [works, setWorks] = useState([]);
const [filterWork, setFilterWork] = useState([]);
const [activeFilter, setActiveFilter] = useState('All');
const [animateCard, setAnimateCard] = useState({ y: 0, opacity: 1 });
const [toggle, setToggle] = useState(false);
useEffect(() => {
const query = '*[_type == "works"]';
client.fetch(query).then((data) => {
setWorks(data);
setFilterWork(data);
});
}, []);
const handleWorkFilter = (item) => {
setActiveFilter(item);
setAnimateCard([{ y: 100, opacity: 0 }]);
setTimeout(() => {
setAnimateCard([{ y: 0, opacity: 1 }]);
if (item === 'All') {
setFilterWork(works);
} else {
setFilterWork(works.filter((work) => work.tags.includes(item)));
}
}, 500);
};
return (
<>
<h2 className="portfolio-head-text">My <span>Portfolio</span></h2>
<div className="app__work-filter">
{['UI/UX','JavaScript', 'React JS', 'University Projects', 'All'].map((item, index) => (
<div key={index}
onClick={() => handleWorkFilter(item)}
className={`app__work-filter-item app_flex p-text ${activeFilter === item ? 'item-active' : ''}`}>
{item}
</div>
))}
</div>
<motion.div
animate={animateCard}
transition={{duration:0.5, delayChildren: 0.5}}
className="app__work-portfolio"
>
{filterWork.map((work,index) => (
<div className="app__work-card-container" key={index}>
<div className="app__work-item app__flex">
<div className="app__work-img app__flex">
<img src={urlFor(work.imgUrl1)} alt={work.name}/>
<motion.div
whileHover={{opacity:[0,1]}}
transition={{duration: 0.3, ease: 'easeInOut', staggerChildren: 0.6}}
className="app__work-hover app__flex">
<p>{work.descriptionPreview}</p>
<motion.div
whileInView={{scale:1}}
whileHover={{scale:[1,0.9]}}
transition={{duration: 0.2}}
className="app__flex"
>
<button onClick={() => setToggle(true)}>more+</button>
</motion.div>
</motion.div>
</div>
<div className="app__work-content app__flex">
<h4 className="bold-text">{work.title}</h4>
<p className="p-text" style={{marginTop: 10}}>{work.tagView}</p>
</div>
</div>
{toggle &&(
<div className="app__work-big-window">
<div className="window-img-x">
<img classname="window-img" src={urlFor(work.imgUrl1)} alt={work.name}/>
<div><HiX className="window-x" onClick={() => setToggle(false)}/></div>
</div>
<div>
<h4>{work.title}</h4>
<h6>{work.subTitle}</h6>
<div/>
<p>{work.description}</p>
</div>
<div/>
<div>
<h6>Technologies used: </h6>
<p>{work.tech}</p>
</div>
</div>
)}
</div>
))}
</motion.div>
</>
)
}

Hide or show mutliple Divs in Reactjs

I have created a component in my ReactJS app with a Button and a div. My goal is to press the button and show/hide the div, which currently works. But I will re-use the component so I will have multiple Buttons and divs. But I always only want one div to show.
If I press a button, hide all the divs from the same component and show the div from the button I just pressed, otherwise if the button I just pressed div is open hide it. It work the same as Bootstrap's dropdown button, but I prefer not to use Bootstrap's dropdown as I would like to create my own custom button.
I import the below Hide component in my App.js file. It works by hiding or showing the div, but would like to hide all other open div's apart from the button I currently pressed if it is not open yet.
Here is the code I currently have and use my Mycomp twice,
function hide () {
return (
<div>
<Mycomp />
<Mycomp />
</div>
);
}
function Mycomp(){
const[dp, setDp] = useState("none");
const toggle = () => {
if (dp === "none"){
setDp("block")
}else{
setDp("none")
}
}
return(
<div>
<button onClick={toggle}>Test</button>
<div style={{display: dp}}>{dp}</div>
</div>
)
}
export default hide;
You can change your component this way to get what you want . try to run this code to see the result
function Hide() {
const [visibleDivIndex, setVisibleDivIndex] = React.useState(0);
const handleVisibleDiv = id => setVisibleDivIndex(id);
const divArr = [1, 2, 3]; // just to show haw many component we will use
return (
<div>
{divArr.map((item, index) => (
<Mycomp
key={index}
id={index}
visibleDivIndex={visibleDivIndex}
handleVisibleDiv={handleVisibleDiv}
/>
))}
</div>
);
}
function Mycomp({ id, visibleDivIndex, handleVisibleDiv }) {
return (
<div>
<button onClick={e => handleVisibleDiv(id)}>Test</button>
<div style={{ display: id === visibleDivIndex ? "block" : "none" }}>
My Div Content
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<Hide />
</React.StrictMode>,
rootElement
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
The basic idea would be to move the state up to the container.
function Hide() {
const defaultDisplay = () => Array.from({length: 2}).fill(false);
const [displays, setDisplays] = useState(defaultDisplay());
function onClick(id) {
const temp = defaultDisplay();
temp[id] = true;
setDisplays(temp);
}
return (
<div>
{
displays.map((display, i) => {
return <Mycomp display={display} id={i} onClick={onClick} />;
}
}
</div>
);
}
function Mycomp({display, id, onClick}) {
const[dp, setDp] = useState(display);
useEffect(() => {
setDp(display ? 'block' : 'none');
}, [display]);
return(
<div>
<button onClick={() => onClick(id)}>Test</button>
<div style={{display: dp}}>{dp}</div>
</div>
)
}
export default Hide;

Categories