I have 2 components. A Navbar and a Sidebar/Cart. On the Navbar i have a button that opens and closes the Sidebar. I have an event that triggers the clickOutside function and its working. The issue is when i click the cart item when the sidebar is active, the sidebar closes and reopens again.
My App.jsx:
import { useEffect, useState } from "react";
import "./App.css";
import { Navbar } from "./Components/Navbar";
import { ListItem } from "./Components/ListItem";
import { Cart } from "./Components/Cart";
function App() {
const [list, setList] = useState([]);
const [cartActive, setCartActive] = useState(false);
const fetchList = () => {
fetch(``)
.then((res) => res.json())
.then((data) => {
setList(data);
})
.catch((error) => {});
};
useEffect(() => {
fetchList();
}, []);
return (
<>
<Navbar setCartActive={setCartActive} cartActive={cartActive} />
<Cart cartActive={cartActive} setCartActive={setCartActive} />
<div className="main">
<div className="container">
{list.map((item) => {
return <ListItem key={item.id} item={item} />;
})}
</div>
</div>
</>
);
}
export default App;
My Navbar:
import { useEffect, useState } from "react";
import { FaShoppingBag } from "react-icons/fa";
import { useSelector } from "react-redux";
export const Navbar = ({ setCartActive, cartActive }) => {
const cart = useSelector((state) => state.cart.cart);
const [totalItems, setTotalItems] = useState(0);
const fetchTotalItems = () => {
setTotalItems(cart.reduce((acc, curr) => acc + curr.amount, 0));
};
useEffect(() => {
fetchTotalItems();
}, [cart]);
return (
<nav>
<div className="container">
<div className="brand">
Redux<span>Store</span>
</div>
<div className="icon" onClick={() => setCartActive(!cartActive)}>
<FaShoppingBag /> ({totalItems})
</div>
</div>
</nav>
);
};
And my Sidebar/Cart:
import { useSelector } from "react-redux";
import { CartItem } from "./CartItem";
import { useEffect, useRef } from "react";
export const Cart = ({ cartActive, setCartActive }) => {
const cart = useSelector((state) => state.cart.cart);
const cartRef = useRef();
const handleClickOutside = (e) => {
if (cartActive && cartRef.current && !cartRef.current.contains(e.target)) {
setCartActive(false);
}
};
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
() => {
document.removeEventListener("mousedown", handleClickOutside);
};
});
return (
<div className={`cart ${cartActive ? "active" : ""}`} ref={cartRef}>
{cart.map((item) => {
return <CartItem key={item.id} item={item} />;
})}
</div>
);
};
This works but when the user clicks on the cart icon when the sidebar is open the sidebar closes and reopens it. this is because it catches the clickOutside function. how can i stop that clickoutside function on the navbar?
here is a link to codesandbox
I have 2 components. A Navbar and a Sidebar/Cart. On the Navbar i have a button that opens and closes the Sidebar. I have an event that triggers the clickOutside function and its working. The issue is when i click the cart item when the sidebar is active, the sidebar closes and reopens again.
My App.jsx:
import { useEffect, useState } from "react";
import "./App.css";
import { Navbar } from "./Components/Navbar";
import { ListItem } from "./Components/ListItem";
import { Cart } from "./Components/Cart";
function App() {
const [list, setList] = useState([]);
const [cartActive, setCartActive] = useState(false);
const fetchList = () => {
fetch(`https://fakestoreapi.com/products`)
.then((res) => res.json())
.then((data) => {
setList(data);
})
.catch((error) => {});
};
useEffect(() => {
fetchList();
}, []);
return (
<>
<Navbar setCartActive={setCartActive} cartActive={cartActive} />
<Cart cartActive={cartActive} setCartActive={setCartActive} />
<div className="main">
<div className="container">
{list.map((item) => {
return <ListItem key={item.id} item={item} />;
})}
</div>
</div>
</>
);
}
export default App;
My Navbar:
import { useEffect, useState } from "react";
import { FaShoppingBag } from "react-icons/fa";
import { useSelector } from "react-redux";
export const Navbar = ({ setCartActive, cartActive }) => {
const cart = useSelector((state) => state.cart.cart);
const [totalItems, setTotalItems] = useState(0);
const fetchTotalItems = () => {
setTotalItems(cart.reduce((acc, curr) => acc + curr.amount, 0));
};
useEffect(() => {
fetchTotalItems();
}, [cart]);
return (
<nav>
<div className="container">
<div className="brand">
Redux<span>Store</span>
</div>
<div className="icon" onClick={() => setCartActive(!cartActive)}>
<FaShoppingBag /> ({totalItems})
</div>
</div>
</nav>
);
};
And my Sidebar/Cart:
import { useSelector } from "react-redux";
import { CartItem } from "./CartItem";
import { useEffect, useRef } from "react";
export const Cart = ({ cartActive, setCartActive }) => {
const cart = useSelector((state) => state.cart.cart);
const cartRef = useRef();
const handleClickOutside = (e) => {
if (cartActive && cartRef.current && !cartRef.current.contains(e.target)) {
setCartActive(false);
}
};
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
() => {
document.removeEventListener("mousedown", handleClickOutside);
};
});
return (
<div className={`cart ${cartActive ? "active" : ""}`} ref={cartRef}>
{cart.map((item) => {
return <CartItem key={item.id} item={item} />;
})}
</div>
);
};
This works but when the user clicks on the cart icon when the sidebar is open the sidebar closes and reopens it. this is because it catches the clickOutside function. how can i stop that clickoutside function on the navbar?
here is a link to codesandbox
Share Improve this question edited 5 hours ago eskimopest asked yesterday eskimopesteskimopest 4353 silver badges19 bronze badges 6- Please share a minimal reproducible example, something that we can copy and test as is. We don't want to make unnecessary assumptions. – Chukwujiobi Canon Commented yesterday
- @ChukwujiobiCanon, i think i did what you asked. – eskimopest Commented yesterday
- Can you please setup a minimum demo in CodeSandbox or any other similar site? – Raghavendra N Commented 7 hours ago
- @RaghavendraN, added a link to codesandbox – eskimopest Commented 5 hours ago
- @eskimopest You don't have the cart item in your CodeSandbox demo. Where should I click to reproduce close and open behaviour? – Raghavendra N Commented 4 hours ago
1 Answer
Reset to default 0The issue is you are changing the active state from both Sidebar and Navbar components. So when you click on the icon in the navbar, first handleClickOutside
function is triggered, which closes the sidebar and immediately onClick
function in Navbar component is triggered which opens it.
One quick solution is exclude the Navbar button in the handleClickOutside
function.
const handleClickOutside = (e) => {
if (
isActive &&
sidebarRef.current &&
!sidebarRef.current.contains(e.target) &&
e.target !== document.getElementsByClassName("icon")[0]
) {
setIsActive(false);
}
};
Check this CodeSandbox demo. In the demo I also changed the mousedown
event to click
event.