GithubHelp home page GithubHelp logo

Comments (37)

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024 1

I would like to work on this issue, can you please assign it to me?

assigned to you @sumitshinde-84

from react.

kushhingol avatar kushhingol commented on April 24, 2024 1

Also a quick suggestion @RahulMittal18 please use conditional rendering instead of styles rendering. Its always better to not mount the required elements/nodes into the DOM tree which makes things less expensive on rendering side. Just dropping in a suggestion

const Navbar = () => {
  const [isOpen, setIsOpen] = useState(false);
  const imgRef = useRef();

  const handleImageClick = () => {
    setIsOpen((prevIsOpen) => !prevIsOpen);
  };

  useEffect(() => {
    const handleClickOutside = (evt) => {
      if (imgRef?.current && !imgRef?.current?.contains(evt.target)) {
        setIsOpen(false);
      }
    };
    document.addEventListener("click", handleClickOutside);
    
    // cleanup for event listener
    return () => {
      document.removeEventListener("click", handleClickOutside);
    };
  }, [isOpen]);

  return (
    <>
      {isOpen && (
        <div className={"dropdown"}>
          <Dropdown />
        </div>
      )}

      <div className="profile_icon_div" ref={imgRef} onClick={handleImageClick}>
        <img
          src={profileImageUrl}
          onClick={() => setIsOpen((prev) => !prev)}
          className="profile_icon_image"
          alt="Profile-Icon"
        />
      </div>
    </>
  );
};

You can also create a custom hook where you can pass the ref and your callback method which can manage the click events which are clicked outside the ref which you pass in the hook.
Happy Learning 😊

from react.

Animesh239 avatar Animesh239 commented on April 24, 2024

you can use the elseif for targeting the same icon when dropdown is active

useEffect(() => {
  let closeDropdown = (e) => {
    if (imgRef.current && !imgRef.current.contains(e.target)) {
      setIsOpen(false);
    } else if (imgRef.current === e.target) {
      setIsOpen((prev) => !prev);
    }
  };
  document.body.addEventListener("click", closeDropdown);
  return () => document.body.removeEventListener("click", closeDropdown);
}, []);

from react.

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024

Still not working @Animesh239

from react.

yogesh-v80 avatar yogesh-v80 commented on April 24, 2024

useEffect(() => {

let closeDropdown = (e) => {
  if (imgRef.current && !imgRef.current.contains(e.target)) {
    setIsOpen(false);
  }
};
document.body.addEventListener("click", closeDropdown);
return () => document.body.removeEventListener("click", closeDropdown);
}, [isOpen]);

// there I added [isOpen] in the dependency array of useEffect
// whenever isOpen will change useEffect will be called with its sideeffect
// I think it will work.

.
.
.
.

// And yeah one more thing is that , there is an extra div tag

return (
   <div>
     <div>
          <div className={"dropdownn1 " + (isOpen ? "open" : "closed")}>
          <Dropdown />
      </div>
      <div className="profile_icon_div" ref={imgRef}>
             <img
                src={profileImageUrl}
                onClick={() => setIsOpen((prev) => !prev)}
                className="profile_icon_image"
                alt=""
              />
       </div>
     </div>
  );

from react.

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024

Still not working guys @Animesh239 @iRONiCBAT7 . Ignore extra div, there is no extra div in the real code. It just got extra here in copying.

my updated useEffect which is not working stll:

 useEffect(() => {
    let closeDropdown = (e) => {
      if (imgRef.current && !imgRef.current.contains(e.target)) {
        setIsOpen(false);
      } else if (imgRef.current === e.target) {
        setIsOpen((prev) => !prev);
      }
    };
    document.body.addEventListener("click", closeDropdown);
    return () => document.body.removeEventListener("click", closeDropdown);
  }, [isOpen]);

from react.

ag533 avatar ag533 commented on April 24, 2024

You are using imgRef but I can't see it defined anywhere. I can only see the state defined not the imgRef. If this is just the copy paste mistake from the original code then it's fine ignore this comment and if not please add const ref = useRef(); below the state.

Also you can change the if condition to something like !imgRef?.current?.contains(e.target) this is more concise and easy to read and make the code look better.

Please first check that the state is changing when you click on the image from isOpen === true to isOpen === false because it is possible it is not change. To make the state change easy just use the following onClick={() => { setIsOpen(!isOpen); }} this will help to change to opposite state when the image is clicked easily. According to what i can see the problem is maybe with the css class name className={"dropdownn1 " + (isOpen ? "open" : "closed") and it may not be assigning names properly. As an alternate to this you can simply remove the line and pass the isOpen state as a prop to the <Dropdown /> menu and use something like this inside the component const Dropdown = ({ open }) => { return ( <div > {open && <div className="dropdown_container"></div>} </div>);}

from react.

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024

None of them worked @ag533
IsOpen remains true even when I click on profile icon again and again.

from react.

ag533 avatar ag533 commented on April 24, 2024

Create a handleClick function to set state. Something like

Def handleClick = () => {
setIsOpen(!open)
}

An call this function in onClick rather than setting the state directly in onClick. As setting the state directly can lead error in the code - see this article - https://refine.dev/blog/common-usestate-mistakes-and-how-to-avoid/

from react.

umeh-promise avatar umeh-promise commented on April 24, 2024

Firstly boss, you did not reference the imgRef.
It should be const imageRef = React.useRef()

import React, {useState, useRef} from 'react'
const Navbar = () => {
const [isOpen, setIsOpen] = useState(false);
const imgRef = useRef()

useEffect(() => {
const closeDropdown = (e) => {
if (imgRef.current && !imgRef.current?.contains(e.target)) {
setIsOpen(false);
}
};
document.body.addEventListener("click", closeDropdown);
return () => document.body.removeEventListener("click", closeDropdown);
}, []);

return (
   <div>
     <div>
          <div className={"dropdownn1 " + (isOpen ? "open" : "closed")}>
          <Dropdown />
      </div>
      <div className="profile_icon_div" ref={imgRef}>
             <img
                src={profileImageUrl}
                onClick={() => setIsOpen((prev) => !prev)}
                className="profile_icon_image"
                alt=""
              />
       </div>
     </div>
  );

};

export default Navbar;

Also do confirm from you dev tools to see if the state of isOpen changes on click (i.e from false to true)

from react.

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024

Updated the original code @umeh-promise ..check now..still not working

from react.

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024

tried this too @ag533 .. still not working

from react.

ag533 avatar ag533 commented on April 24, 2024

Hey @RahulMittal18,

Try this solution it's working fine for me - https://codesandbox.io/s/quiet-sky-s85kqe?file=/src/App.js. It is similar to you solution but without the overhead complications of the CSS 😄. Let me know if this helps.

All the best 😸

from react.

rachit298 avatar rachit298 commented on April 24, 2024

@RahulMittal18 Please share all dependencies of this file, like the CSS code you used in this file and the Dropdown component's code.

from react.

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024

@ag533 read the complete expected behaviour once again.
Dropdown is not closing on clicking outside the navbar

from react.

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024

@rachit298 It is not possible to share here. At max, I can schedule a meet for this.

from react.

rachit298 avatar rachit298 commented on April 24, 2024

@RahulMittal18 You can create a repo and put all files in there. And share that repo link in original issue here.

from react.

ag533 avatar ag533 commented on April 24, 2024

@RahulMittal18 I have modified the sandbox respective to the expected behaviour - https://codesandbox.io/s/quiet-sky-s85kqe?file=/src/withClickOutside.js:0-574. Please have a look and let me know if this works for you. Basically what i have done is created a wrapper for the component that can be implemented for the whole page.

from react.

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024

@ag533 it's working but i can't impplement it in my code

from react.

ag533 avatar ag533 commented on April 24, 2024

In that case please share bits of your code in a sandbox in a runnable state so that I can see it over there and help you @RahulMittal18 😊. As it is very difficult to replicate your exact behaviour in my system.

from react.

gaearon avatar gaearon commented on April 24, 2024

Can someone please create a sandbox for the original code? I see there are many solutions. I want to post the idiomatic one but I don't have any sandbox to modify because they're all doing something different.

from react.

umeh-promise avatar umeh-promise commented on April 24, 2024

Alright @RahulMittal18 can you please share the code sandbox for this so we can easily debug the code.

from react.

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024

@umeh-promise @gaearon @ag533 @rachit298 sharing my code sandbox link.. Help me out guys

https://codesandbox.io/s/festive-oskar-gyry34?file=/src/App.js

from react.

umeh-promise avatar umeh-promise commented on April 24, 2024

@RahulMittal18
To fix the issue, I added the stopPropagation() method to the child element's event handler function handleProfileClick() to prevent the event from propagating up to the parent elements. This ensured that the parent event handler function closeDropdown(), which was closing the dropdown menu, was not triggered when the user clicked inside the menu. I also changed document.body.addEventListener() to document.addEventListener() in the closeDropdown() function to close the dropdown menu when the user clicks anywhere in the document, not just in the NavbarNew component.

I also updated the setIsOpen() method inside the handleProfileClick() function to toggle the isOpen state based on its previous value using the prevState function argument. This ensures that the isOpen state is updated correctly, even if multiple updates are triggered in quick succession.

With these changes, the dropdown menu now stays open when the user clicks inside the menu or anywhere in the document, but still closes when the user clicks outside the menu.

All changes were made in the NavbarNew component.

(https://codesandbox.io/s/amazing-khorana-2pqz7d?file=/src/components/NavbarNew.js)

from react.

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024

@umeh-promise document.addEventListener is not working on my localhost. even the dropdown does not get open..
after using document.body.addEventListener it is working as before not like yours in code sandbox

from react.

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024

@umeh-promise I changed "click" with "mouseup"...now its working as before with document.addEventListener but not working as expected.

Changes I did:

  const handleProfileClick = (e) => {
    e.stopPropagation();
    // I ammended this line of code from setIsOpen(!isOpen) to setIsOpen((prevState) => !prevState);
    // setIsOpen((prevState) => !prevState) toggles the state from true to false
    setIsOpen((prevState) => !prevState);
  };

useEffect(() => {
    const closeDropdown = (e) => {
      // console.log(
      //   e.target,
      //   imgRef.current,
      //   isOpen,
      //   e.path,
      //   imgRef.current.contains(e.target)
      // );
      if (imgRef.current && !imgRef.current?.contains(e.target)) {
        setIsOpen(false);
        // console.log("entered");
      }
    };
    document.addEventListener("mouseup", closeDropdown);
    return () => document.removeEventListener("mouseup", closeDropdown);
  }, [isOpen]);

from react.

umeh-promise avatar umeh-promise commented on April 24, 2024

This actually might be from your local machine
Did you try it using the link I attached here.
Test it out it's working.
https://codesandbox.io/s/amazing-khorana-2pqz7d?file=/src/components/NavbarNew.js

from react.

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024

@umeh-promise code in your link is working which has react-version 18 and i had react 16..upgrading it to 18 is causing many dependency conflicts. so i am not able to upgrade it to 18

from react.

RahulMittal18 avatar RahulMittal18 commented on April 24, 2024

@umeh-promise give a solution for react version 16.13.1

from react.

pranshu05 avatar pranshu05 commented on April 24, 2024

Adding if statement for isOpen can solve the problem

const Navbar = () => {
  const [isOpen, setIsOpen] = useState(false);
  const imgRef = useRef();

  useEffect(() => {
    let closeDropdown = (e) => {
      if (imgRef.current && !imgRef.current.contains(e.target)) {
        setIsOpen(false);
      }
    };

    document.body.addEventListener("click", closeDropdown);

    return () => document.body.removeEventListener("click", closeDropdown);
  }, []);

  const toggleDropdown = () => {
    setIsOpen((prev) => !prev);
  };

  return (
    <div>
      <div className={"dropdownn1 " + (isOpen ? "open" : "closed")}>
        <Dropdown />
      </div>
      <div className="profile_icon_div" ref={imgRef}>
        <img
          src={profileImageUrl}
          onClick={toggleDropdown}
          className="profile_icon_image"
          alt=""
        />
      </div>
    </div>
  );
};

export default Navbar;

from react.

sumitshinde-84 avatar sumitshinde-84 commented on April 24, 2024

I would like to work on this issue, can you please assign it to me?

from react.

gokuljs avatar gokuljs commented on April 24, 2024

@RahulMittal18 instead of using a document.body use window.addEventListener that should solve your issue

can you please add a border to this both div tags and check are you clicking outside the div
image

you can use this has your reference
https://codesandbox.io/s/frosty-grass-sx0zty?file=/src/App.tsx

from react.

sumitshinde-84 avatar sumitshinde-84 commented on April 24, 2024

const Navbar = () => {
const [isOpen, setIsOpen] = useState(false);
const imgRef = useRef();

useEffect(() => {
let closeDropdown = (e) => {
if (imgRef.current && !imgRef.current.contains(e.target)) {
setIsOpen(false);
}
};

document.body.addEventListener("click", closeDropdown);
return () => document.body.removeEventListener("click", closeDropdown);

}, []);

const handleProfileIconClick = () => {
setIsOpen((prev) => !prev); // Toggle the dropdown
};

return (


<div className={"dropdownn1 " + (isOpen ? "open" : "closed")}>



<img
src={profileImageUrl}
onClick={handleProfileIconClick} // Update the event handler
className="profile_icon_image"
alt=""
/>


);
};

export default Navbar;

it will definitely work try this

from react.

umeh-promise avatar umeh-promise commented on April 24, 2024

@RahulMittal18 The only way I could be of help to you is if you do add me to the project repo.
That way I can have access to the React 16 version you are using and do the debugging or better still, send me the project link.

Thank you.

from react.

Sukrittt avatar Sukrittt commented on April 24, 2024

You can try adding a if condition to check if you are clicking the profile icon. If yes then toggle isOpen
Note that you are not mentioning anything about what will happen if we click on the profile image (imgRef).

Here is my code:

const Navbar = () => {
const [isOpen, setIsOpen] = useState(false);
const imgRef = useRef();
useEffect(() => {
let closeDropdown = (e) => {
if (imgRef.current && !imgRef.current.contains(e.target)) {
setIsOpen(false);
}

 //Here's what I added
 if(imgRef.current.contains(e.target)){
     setIsOpen((prev) => !prev);
 }

};
document.body.addEventListener("click", closeDropdown);
return () => document.body.removeEventListener("click", closeDropdown);
}, []);


return (
   <div>
          <div className={"dropdownn1 " + (isOpen ? "open" : "closed")}>
                <Dropdown />
          </div>
          <div className="profile_icon_div" ref={imgRef}>
                 <img
                    src={profileImageUrl}
                    onClick={() => setIsOpen((prev) => !prev)}
                    className="profile_icon_image"
                    alt=""
                  />
           </div>
    </div>
  );

};

export default Navbar;

from react.

urmybestfriend avatar urmybestfriend commented on April 24, 2024

Perfect guys

from react.

sandeepmichael avatar sandeepmichael commented on April 24, 2024

why don't you add conditional rendering for onClick method. just correct me if i'm wrong guys.

from react.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.