Here there everyone. I've a dropdown component in which I'm passing options and on clicking of those options I'm importing 3d models with R3F. so its working fine but the only catch is when the page loads for the first time first 2 or 4 clicks import totally random models ones that I dont indent to import but its happening. below is the code please take a look what's wrong with it.
import {
useState,
useEffect,
useRef,
Dispatch,
SetStateAction,
} from "react";
const useOnClickOutside = <T extends HTMLElement>(
ref: React.RefObject<T>,
handler: () => void
) => {
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
handler();
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [ref, handler]);
};
interface Option {
name: string;
value: string | number;
image: string;
id: string;
}
interface DropdownProps {
options: Option[];
title: string;
xAxis: number;
yAxis: number;
zAxis: number;
SysmexArray: number[][];
setSysmexArray: Dispatch<SetStateAction<number[][]>>;
cr10_no_7thArray: number[][];
setCr10_no_7thArray: Dispatch<SetStateAction<number[][]>>;
ID: string;
setID: Dispatch<SetStateAction<string>>;
}
const Dropdown: React.FC<DropdownProps> = (props) => {
const [isOpen, setIsOpen] = useState(false);
const [selectedOption, setSelectedOption] = useState<Option | undefined>(
undefined
);
const dropdownRef = useRef<HTMLDivElement>(null);
const toggleDropdown = () => setIsOpen(!isOpen);
useOnClickOutside(dropdownRef, () => setIsOpen(false));
useEffect(() => {
const handleEscapeKeyPress = (event: KeyboardEvent) => {
if (event.key === "Escape") {
setIsOpen(false);
}
};
document.addEventListener("keydown", handleEscapeKeyPress);
return () => {
document.removeEventListener("keydown", handleEscapeKeyPress);
};
}, []);
/****************************************/
/*THIS FUNCTION IS NOT WORKING PROPERLY */
/**************************************** */
const handleOptionClick = (option: Option) => {
setSelectedOption(option);
setIsOpen(false);
if (selectedOption?.name === "m-st") {
props.setID(() =>{
props.setSysmexArray(() => [
...props.SysmexArray,
[props.xAxis, props.yAxis, props.zAxis],
]);
return"m-st"});
}
if (selectedOption?.name === "CR10") {
props.setID(() => {
props.setCr10_no_7thArray(() => [
...props.cr10_no_7thArray,
[props.xAxis, props.yAxis, props.zAxis],
]);
return"r-cr10"});
}
};
return (
<div className="relative pl-0" ref={dropdownRef}>
<button
type="button"
className="flex items-center justify-between w-full px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
onClick={toggleDropdown}
>
{selectedOption ? (
<>
{props.title}
{console.log(`${selectedOption.name} : ${selectedOption.value}`)}
</>
) : (
props.title
)}
<svg
className={`w-5 h-5 ml-2 transition-transform ${
isOpen ? "transform rotate-180" : ""
}`}
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
</button>
{isOpen && (
<div className="absolute z-10 w-full mt-2 bg-white rounded-md shadow-lg">
<ul className="py-1">
{props.options.map((option) => (
<li
key={option.value}
className="flex flex-col justify-center items-center px-4 py-2 text-sm text-gray-700 cursor-pointer hover:bg-gray-100"
onClick={() => handleOptionClick(option)}
>
<img
src={option.image}
height={100}
width={100}
alt={option.name}
/>{" "}
<div className="text-center">{option.name}</div>
</li>
))}
</ul>
</div>
)}
</div>
);
};
export default Dropdown;
I've tried changing dependecy arrrays of useEffect, using useCallback to make sure it fire only what is got clicked but neither of them worked for me.
Use option
argument instead of selectedOption
state otherwise your function will use previous value of selectedOption
. Also we don't know what setID
function exactly does so this is a shot in dark. But try below and see if it works.
const handleOptionClick = (option: Option) => {
setSelectedOption(option);
setIsOpen(false);
if (option?.name === "m-st") {
props.setID(() =>{
props.setSysmexArray(() => [
...props.SysmexArray,
[props.xAxis, props.yAxis, props.zAxis],
]);
return"m-st"});
}
if (option?.name === "CR10") {
props.setID(() => {
props.setCr10_no_7thArray(() => [
...props.cr10_no_7thArray,
[props.xAxis, props.yAxis, props.zAxis],
]);
return"r-cr10"});
}
};