最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

reactjs - React stop click outside event - Stack Overflow

programmeradmin2浏览0评论

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
 |  Show 1 more comment

1 Answer 1

Reset to default 0

The 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.

发布评论

评论列表(0)

  1. 暂无评论