I'm using Strapi and Next.js to create a Dropdown Menu, but when i hover on a menu button, all dropdown container of the menu show up.
Can anybody show me the way to do it right? Thank you very much!
This is my code:
const Navbar = ({ navbar }) => {
const [open, setOpen] = useState(false);
const [mobileMenuIsShown, setMobileMenuIsShown] = useState(false);
const handleClick = () => {setOpen(!open)};
const onMouseEnter = () =>{
if (window.innerWidth < 960) {
setOpen(false);
} else {
setOpen(true);
}
};
const onMouseLeave = () =>{
if (window.innerWidth < 960){
setOpen(false);
} else {
setOpen(false);
}
};
return (
<>
<nav>
<div>
{navbar.topmenu.map((navMenu) => (
<div
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
key={navMenu.id}
>
<button
onClick={handleClick}>
<span class="mr-1">{navMenu.label}</span>
</button>
{open &&
<ul>
{navMenu.submenu.map((link) => (
<li key={link.id}>
<CustomLink link={link}>{link.text}</CustomLink>
</li>
))}
</ul>
}
</div>
))}
</div>
</nav>
</>
Related
So I have those cards on the link right now. When the favorite button is clicked it will appear under FAVORITES. I wanted those cards under FAVORITES to be on the local storage, and when I "unfavorite" the cards, they should also be removed from the local storage. But how exactly do I do that? Should I use useEffect()?
Codesandbox: https://codesandbox.io/s/broken-fast-kgyzvc?file=/src/App.js
App.js
export default function App() {
const [all, setAll] = useState(true);
const [favorites, setFavorites] = useState(false);
const showAll = () => {
setAll(true);
setFavorites(false);
};
const showFavorite = () => {
setFavorites(true);
setAll(false);
};
const [dataSource, setDataSource] = useState(data);
const onFavorite = (cardId) => {
const newDataSource = [...dataSource];
const foundCardData = newDataSource.find((card) => card.id === cardId);
if (!foundCardData) return;
foundCardData.isFavorite = !foundCardData.isFavorite;
setDataSource(newDataSource);
};
return (
<div className="App">
<div className="button-group">
<button className={all ? "all active" : "all"} onClick={showAll}>
ALL
</button>
<button
className={favorites ? "favorites active" : "favorites"}
onClick={showFavorite}
>
FAVORITES
</button>
</div>
<br />
<Masonry
breakpointCols={3}
className="my-masonry-grid"
columnClassName="my-masonry-grid_column"
>
{all &&
dataSource.map((item) => (
<Cards
key={item.id}
text={item.text}
isFavorite={item.isFavorite}
onFavorite={() => onFavorite(item.id)}
/>
))}
{favorites &&
dataSource
.filter((item) => item.isFavorite === true)
.map((filtered) => (
<Cards
key={filtered.id}
text={filtered.text}
isFavorite={filtered.isFavorite}
onFavorite={() => onFavorite(filtered.id)}
/>
))}
</Masonry>
</div>
);
}
Cards.js
const Cards = ({ text, isFavorite, onFavorite }) => {
return (
<div className="cards">
<p>{text}</p>
<button onClick={onFavorite}>
{isFavorite ? "Added to Favorites!" : "Favorite"}
</button>
</div>
);
};
I'm trying to close the open menu once an outside click is triggered. I managed to implement it on a single button but now as I want to map a list of buttons, I can't manage to open any of the button menus.
import "./navbar.css";
import React, { useEffect, useRef, useState } from "react";
import { MenuItems } from "./menuItems";
export const Navbar = () => {
const [toggle, setToggle] = useState(true);
const btnRef = useRef();
const handleClick = () => {
setToggle(!toggle);
};
useEffect(() => {
const closeDropdown = (e) => {
if (e.path[0] !== btnRef.current) {
setToggle(false);
}
};
document.body.addEventListener("click", closeDropdown);
return () => {
document.body.removeEventListener("click", closeDropdown);
};
}, []);
return (
<div>
<div className="menu1">
<i className="fa fa-home" id="home"></i>
{MenuItems.map((n, i) => (
<li key={i} className="list">
<button ref={btnRef} onClick={handleClick} className="btn1">
{n.name}
<i className="fa fa-caret-down"></i>
</button>
</li>
))}
</div>
<div className={toggle ? "d-active" : "d-inactive"}>
<div className="dropdown">Empty</div>
</div>
</div>
);
};
My approach to this would be to have an OutsideDetector wrapper, with outsideClickHandler.
OutsideClickWatcher.js
const OutsideClickWatcher = (props) => {
const { onClickOutside } = props;
const wrapperRef = useRef(null);
useEffect(() => {
function handleClickOutside(event) {
if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
onClickOutside();
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [wrapperRef, onClickOutside]);
return <div ref={wrapperRef}>{props.children}</div>;
};
Navbar.js
export const Navbar = () => {
const [toggle, setToggle] = useState(true);
const btnRef = useRef();
const handleClick = () => {
setToggle(!toggle);
};
return (
<div>
<div className="menu1">
<i className="fa fa-home" id="home"></i>
{MenuItems.map((n, i) => (
<OutsideClickWatcher key={i} onClickOutside={() => setToggle(false)}>
<li className="list">
<button ref={btnRef} onClick={handleClick} className="btn1">
{n.name}
<i className="fa fa-caret-down"></i>
</button>
</li>
</OutsideClickWatcher>
))}
</div>
<div className={toggle ? "d-active" : "d-inactive"}>
<div className="dropdown">Empty</div>
</div>
</div>
);
};
Why do I prefer this approach? It gives me the flexibility to pass custom outside click handlers, and it's very reusable for all sorts of ui elements.
Note that this is a boilerplate code, it can obviously be improved further.
I have this Little list that should show different modals based on what the user select , and the problem is When I select the first time it doesn't work and I have to click on it again to work .
Here is my code :
const Mylist = props => {
const [open2, setOpen2] = React.useState(false);
const [open3, setOpen3] = React.useState(false);
const handleClose2 = () => {
setOpen2(false);
};
const handleOpen2 = () => {
setOpen2(true);
};
const handleClose3 = () => {
setOpen3(false);
};
const handleOpen3 = () => {
setOpen3(true);
};
const [isActive , SetIsActive] = useState(false);
const option =["Paid" , "UnPaid"]
return (
<div>
<i class="fas fa-ellipsis-v" onClick={(e) => SetIsActive(!isActive)}></i>
{isActive && (
<div>
{option.map((option) => (
<div
onClick={(e) => {
SetIsActive(false);
{option == 'Paid' && setOpen2(true)}
{option == 'UnPaid' && setOpen3(true)}
}}
>
{option}
</div>
))}
<Modal onClose={handleClose2} open={open2} >
<div>
Content
</div>
</Modal>
<Modal onClose={handleClose3} open={open3} >
<div>
Content
</div>
</Modal>
Your code block is a bit messy with some brackets and closing tags missing (possibly some of the code was not copied in by mistake?).
In any case, I did my best at trying to fill in the missing parts and did some refactoring to your code. I hope that fixes your bug!
import React, { useState } from 'react';
const MyList = (props) => {
const [isOpen1, setIsOpen1] = useState(false);
const [isOpen2, setIsOpen2] = useState(false);
const [isActive, setIsActive] = useState(false);
const openOption = (option) => {
setIsActive(false);
if (option === "Paid") {
setIsOpen1(true);
}
if (option === "UnPaid") {
setIsOpen2(true);
}
};
const options =["Paid", "UnPaid"]
return (
<div>
<i class="fas fa-ellipsis-v" onClick={() => setIsActive(!isActive)}></i>
{isActive && (
<div>
{options.map((option) => (
<div onClick={() => openOption(option)}>
{option}
</div>
))}
</div>
)}
<Modal onClose={() => setIsOpen1(false)} open={isOpen1} >
<div>
Content
</div>
</Modal>
<Modal onClose={() => setIsOpen2(false)} open={isOpen2} >
<div>
Content
</div>
</Modal>
</div>
);
};
I'm working on an ecommerce site. Currently, I have a page which maps over all of the items from the API and displays them on screen.
I'm trying to make it so that when one of the mapped items is clicked, the user will be taken to a new page ("Item") featuring just that item. I'm using React Router, but it's not working.
Any advice to get me in the right direction on how to implement this would be appreciated.
Please specifically see the return statement and how I added the Link routers.
import React, { useState, useEffect } from 'react';
import './../App.css';
import * as ReactBootStrap from 'react-bootstrap';
import {Link} from 'react-router-dom';
function Shop() {
const [products, setProducts] = useState([]);
const [filterProducts, setFilteredProducts] = useState([]);
const [item, setItem] = useState('');
const [currentSort, setCurrentSort] = useState('');
const [loading, setLoading] = useState(false);
useEffect(async () => {
fetchItems();
}, [])
const fetchItems = async () => {
const data = await fetch('https://fakestoreapi.com/products');
const items = await data.json();
setProducts(items)
setLoading(true)
}
function priceUSD(change){
return change.toFixed(2)
}
useEffect(() => {
const filteredItems = products.filter((a) => {
if (item === '') {return a} else {return a.category === item}
});
setFilteredProducts(filteredItems);
}, [item, products])
useEffect(() => {
if (currentSort === '') {
return
}
const sortedItems = filterProducts.sort((a, b) => {
return currentSort === 'ASE' ? a.price - b.price : b.price - a.price
});
setFilteredProducts([...sortedItems]);
}, [currentSort])
return (
<div>
<div className="itemSort">
<p onClick={() => setItem("")}>All items</p>
<p onClick={() => setItem("men clothing")}>Men clothing</p>
<p onClick={() => setItem("women clothing")}>Women clothing</p>
<p onClick={() => setItem("jewelery")}>Jewelery</p>
<p onClick={() => setItem("electronics")}>Electronics</p>
</div>
<div className="itemSort">
<p>Order by price</p>
<p onClick={() => setCurrentSort('DESC')}>Highest</p>
<p onClick={() => setCurrentSort('ASE')}>Lowest</p>
</div>
<div className="gridContainer">
{loading ? <Link to="/Item">
(filterProducts.map((a, index) => (
<div key={index} className="productStyle">
<img src={a.image} className="productImage"></img>
<p>{a.title}</p>
<p>${priceUSD(a.price)}</p>
</div>
))) </Link> : (<ReactBootStrap.Spinner className="spinner" animation="border" />)
}
</div>
</div>
)
}
export default Shop;
You need a route like /item/:id to have one page for one item and assuming that a product has an id:
<div className="gridContainer">
{loading ?
(filterProducts.map((a, index) => (
<Link to={`/Item/${a.id}`}>
<div key={index} className="productStyle">
<img src={a.image} className="productImage"></img>
<p>{a.title}</p>
<p>${priceUSD(a.price)}</p>
</div>
</Link>
:
(<ReactBootStrap.Spinner className="spinner" animation="border" />)
}
</div>
My problem might be more related to js syntax, but the thing is, in material-ui we receive the js code for the Modal function, so how, from another file, does one calls the modal to be opened on a button click (in this case it onRowClicked in a table)
The Modal.js file with relevant code is:
export default function TransitionsModal() {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
The MainTable.js code with the relevant code is:
const openPopup = rowData => {
console.log(rowData.EBELN);
TransitionsModal().setOpen(true); <---- Trying to call the Modal and open it
}
const MainTable = props => {
return (
<div className="col-md-12">
<div className="feed-toggle">
<ul className="nav nav-pills outline-active">
</ul>
</div>
<MaterialTable
columns = {columnsSetup}
options = {{ NOT_RELATED }}
onRowClick={(event, rowData) => openPopup(rowData)} -> calls Open Popup
data={query => NOT_RELATED
});
})
})
}
/>
</div>
);
};
I think that you can try to control the TransitionModal from your MainList component :
const MainTable = props => {
const [showModal , setModal] = useState;
const handleModal = status => {
setModal(status);
}
return (
<div className="col-md-12">
<TransitionsModal
showModal={showModal}
handleModal={ handleModal }
/>
<div className="feed-toggle">
<ul className="nav nav-pills outline-active">
</ul>
</div>
<MaterialTable
columns = {columnsSetup}
options = {{ NOT_RELATED }}
onRowClick={(event, rowData) => openPopup(rowData)} -> calls Open Popup
data={query => NOT_RELATED
});
})
})
}
/>
</div>
);
};
And in your TransitionModal component you just have to pass the prop to control if it will show or not 'showModal' and the method that will allow it to close it handleModal, check that ive made one method that can handle open and close, just by passing the status parameter:
export default function TransitionsModal( { showModal, handleModal } ) {
const classes = useStyles();
return (
<div>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={ showModal }
onClose={ () => { handleModal(false) } }
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>