Reputation: 1328
I have made a dropdown in my react js project with list items in it (Dropdown items are shown by ul and li tags here).
The issue i am facing is that on selecting any item the value in state changes but the dropdown doesn't close not do it closes when i click anywhere outside of it. Please help me out, here is the working codesandbox url for the same repo
Also i am sharing the code below .
App.js
import React, { Component, useState } from "react";
import Dropdown from "./dropdown";
import "./dropdown.css";
const App = () => {
const [value, setValue] = useState("");
const [showDropdown, setShowDropdown] = useState(false);
const selectedValue = (value) => {
console.log(value);
setValue(value);
};
const onSelectItem = () => {
console.log("12");
setShowDropdown(false);
};
return (
<section className="shadow-border" id="FlightsForm">
<div className="top-sec d-flex">
<div className="left-side">
<span
className="custm-dropdown"
onClick={() => setShowDropdown(true)}
>
<span style={{ backgroundColor: "grey", color: "white" }}>
{value.tripType}
</span>
<span className="dropdown-icon"> </span>
<Dropdown
selectedValue={selectedValue}
onSelectItem={onSelectItem}
showDropdown={showDropdown}
/>
</span>
</div>
</div>
</section>
);
};
export default App;
Dropdown.js
import React from "react";
import { useState, useEffect } from "react";
const TripTypeDropdown = (props) => {
const [values, setValues] = useState([
{ tripType: "One Way", value: 1 },
{ tripType: "Return", value: 2 },
{ tripType: "Multi- City", value: 3 },
]);
const [selectedItem, setSelectedItem] = useState({
tripType: "One Way",
value: 1,
});
useEffect(() => {
props.selectedValue(selectedItem);
console.log(selectedItem);
}, [selectedItem]);
const selectItemFromList = (index) => {
const itemSelected = values[index];
setSelectedItem(itemSelected);
props.onSelectItem();
};
const getActiveClassName = (item) => {
if (selectedItem) {
if (item.tripType == selectedItem.tripType) return "active";
else return "";
}
} ;
return (
<React.Fragment>
<div
className={`dropdown-modal sm-modal ripple trip-type-dropdown`}
style={{ display: `${props.showDropdown ? "block" : "none"}` }}
>
<ul>
{console.log(values)}
{values.map((item, index) => (
<li
className={getActiveClassName(item)}
onClick={() => selectItemFromList(index)}
key={index}
style={{backgroundColor:'yellow',border:"1px solid black",listStyle:"none"}}
>
{item.tripType}
</li>
))}
</ul>
</div>
</React.Fragment>
);
};
export default TripTypeDropdown;
Upvotes: 1
Views: 788
Reputation: 3900
You need to stop bubbling up of click event from your dropdown component to it's parent span element. On click you need to pass thevent argument and call stopPropagation
function of event object
Here is the condesandbox
Dropdown.js
const selectItemFromList = (e,index) => {
e.stopPropagation();
...
<li
className={getActiveClassName(item)}
onClick={(e) => selectItemFromList(e,index)}
key={index}
Also added code for outside click.
const ref = useRef();
...
useEffect(() => {
document.addEventListener("click", handleDropdownClick);
}, [ref]);
const handleDropdownClick = (e) => {
e.stopPropagation();
if (ref.current && ref.current.contains(e.target)) {
setShowDropdown(true);
} else {
setShowDropdown(false);
}
};
...
<span
ref={ref}
className="custm-dropdown"
onClick={handleDropdownClick}
>
Upvotes: 3
Reputation: 202608
You should stop the propagation of the click event from the list item to the outer span, this is defeating any attempts to toggle the dropdown closed again from the parent.
const selectItemFromList = (index) => (e) => {
e.stopPropagation();
const itemSelected = values[index];
setSelectedItem(itemSelected);
props.onSelectItem();
};
...
<li
key={index}
...
onClick={selectItemFromList(index)}
...
>
{item.tripType}
</li>
To handle outside clicks you attach a React ref to the dropdown div and use an useEffect
hook to add an onClick
event listener to the widow object and check that the onClick
's event target is not contained within the dropdown div.
useEffect(() => {
const outsideClickHandler = (e) => {
if (props.showDropdown && !outsideRef.current?.contains(e.target)) {
props.onSelectItem(false);
}
};
window.addEventListener("click", outsideClickHandler);
return () => window.removeEventListener("click", outsideClickHandler);
}, [props]);
...
<div
ref={outsideRef}
...
>
...
</div>
Upvotes: 1