I am using React, tailwind and Redux to create a dropdown with filtering search. I know there can be other methods to do this like Select tag or using tailwind components already out there however I'm in too deep trying to figure out the issue as EVERYTHING SEEMS TO BE RIGHT.
In summary, when items in the list are not filtered - onClick function attached to the list item works fine. However upon filtering, the onClick function does not work. More specifically- it does not work when chrome devTools is close but it works when it's open. It's a small project of mine (https://fridge-daddy-juiced-up.onrender.com)
you can visit my github repo here: https://github.com/faithyeenxin/fridge-daddy-juiced-up
below is the code in question
import React, { useEffect, useRef, useState } from 'react';
import { getUserId } from '../../app/slices/userSlice';
import { useAppDispatch, useAppSelector } from '../../app/store';
import { ICategory } from '../../interface';
import { capitalizeWords } from '../utility/functions/capitalizeWord';
import DropdownOption from './DropdownOption';
import { format, min, max, add } from 'date-fns';
import {
filterCategories,
getShelfLife,
resetShelfLife,
setShelfLife,
showCategories,
showFilteredCategories,
} from '../../app/slices/categoriesSlice';
import { toast } from 'react-toastify';
import { sortedIndex } from 'lodash';
interface IShelfLife {
id: number;
name: string;
days: number;
}
interface IDropdownProps {
name: string;
purchaseDate: any;
setExpiryDate: any;
setDaysInFocus: any;
newItem: any;
setNewItem: any;
resetState: any;
}
const DropdownSelect = ({
name,
purchaseDate,
setExpiryDate,
setDaysInFocus,
newItem,
setNewItem,
resetState,
}: IDropdownProps) => {
const divRef: any = useRef(null);
const [openDropdown, setOpenDropdown] = useState(false);
const [selectedValue, setSelectedValue] = useState<IShelfLife | ICategory>();
const dispatch = useAppDispatch();
const categories = useAppSelector(showCategories);
const filteredCategories = useAppSelector(showFilteredCategories);
const shelfLife = useAppSelector(getShelfLife);
const [searchValue, setSearchValue] = useState('');
useEffect(() => {
dispatch(filterCategories(searchValue));
}, [searchValue]);
useEffect(() => {
function handleClickOutside(event: any) {
if (divRef.current && !divRef.current.contains(event.target)) {
// Clicked outside the div, so hide it
dispatch(filterCategories(''));
setOpenDropdown(false);
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
useEffect(() => {
setSelectedValue(undefined);
}, [resetState]);
const handleSelectedValue = (dropdownName: string, item: any) => {
setOpenDropdown(!openDropdown);
setSelectedValue(item);
if (dropdownName === 'Category') {
setNewItem({ ...newItem, categoryId: item.id });
if (item.name !== '-') {
dispatch(setShelfLife(item));
} else {
dispatch(resetShelfLife());
}
}
if (dropdownName === 'Compartment') {
setNewItem({ ...newItem, storedIn: item.name.split(' ')[0] });
setDaysInFocus(item.days);
console.log(item.days);
let newExpiryDate = new Date();
newExpiryDate.setDate(new Date(purchaseDate).getDate() + item.days);
console.log(newExpiryDate);
setExpiryDate(format(newExpiryDate, 'yyyy-MM-dd'));
}
};
const [itemsToRender, setItemsToRender] = useState<
ICategory[] | IShelfLife[]
>();
useEffect(() => {
if (name === 'Category' && filteredCategories.length > 0) {
setItemsToRender(filteredCategories);
} else if (name === 'Category' && filteredCategories.length <= 0) {
setItemsToRender(categories);
} else if (name !== 'Compartment') {
setItemsToRender(categories);
} else {
setItemsToRender(shelfLife);
}
}, [categories, filteredCategories, shelfLife]);
return (
<div className='w-full' ref={divRef}>
<button
id='dropdownSearchButton'
onClick={() => setOpenDropdown(!openDropdown)}
className='relative items-center text-center justify-center inline-flex w-full h=[40px] p-2 rounded-3xl bg-opacity-60 text-md tracking-wide text-white disabled:text-gray-100 placeholder-white bg-mutedPink placeholder:font-bold font-lora focus:bg-opacity-80 focus:outline-none'
data-value={selectedValue?.id}
>
{selectedValue ? capitalizeWords(selectedValue?.name) : name}
<img src='/images/cards/dropdown.svg' className='absolute right-7' />
</button>
{/* <!-- Dropdown menu --> */}
<div className='relative w-full'>
<div
id={`dropdownSearch`}
className={`z-10 ${
openDropdown ? '' : 'hidden'
} rounded-lg shadow absolute bg-extraMutedPink mt-2 inset-0 ${
name === 'Compartment' ? 'h-[100px]' : 'h-[220px]'
}`}
>
{name !== 'Compartment' && (
<div className='p-3'>
<label htmlFor='input-group-search' className='sr-only'>
Search
</label>
<div className='relative'>
<div className='absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none'>
<svg
className='w-5 h-5 text-mutedPink'
aria-hidden='true'
fill='currentColor'
viewBox='0 0 20 20'
xmlns='http://www.w3.org/2000/svg'
>
<path
fillRule='evenodd'
d='M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z'
clipRule='evenodd'
></path>
</svg>
</div>
<input
type='text'
id='input-group-search'
autoComplete='off'
className='block w-full p-1 pl-10 text-md text-mutedPink border border-gray-300 rounded-3xl bg-white placeholder:text-mutedPink placeholder:font-bold font-lora focus:outline-none'
placeholder={`Search ${name}`}
onChange={(e) => setSearchValue(e.target.value)}
/>
</div>
</div>
)}
<ul
className={`h-36 px-3 ${
name === 'Category' ? 'pb-3' : 'py-3'
} overflow-y-auto text-md text-white`}
aria-labelledby='dropdownSearchButton'
>
{itemsToRender?.map((item, idx) => (
<DropdownOption
key={idx}
item={item}
handleSelectedValue={() => handleSelectedValue(name, item)}
/>
))}
</ul>
</div>
</div>
</div>
);
};
export default DropdownSelect;
import React from 'react';
import { useAppDispatch } from '../../app/store';
import { ICategory } from '../../interface';
import { capitalizeWords } from '../utility/functions/capitalizeWord';
import { format, min, max, add } from 'date-fns';
import { resetShelfLife, setShelfLife } from '../../app/slices/categoriesSlice';
import { toast } from 'react-toastify';
const DropdownOption = ({ item, handleSelectedValue }: any) => {
const dispatch = useAppDispatch();
return (
<li
key={item.id}
className='flex items-center rounded-2xl pl-2 text-orange font-lora hover:bg-white hover:cursor-pointer'
onClick={handleSelectedValue}
>
{capitalizeWords(item.name)}
</li>
);
};
export default DropdownOption;
Please help! Even ChatGPT has failed me. I have tried methods like event delegation but it did not work as well (or i could have not executed it well)
Edit: it then works fine when I deploy it and search categories on my phone (could it be because my system is slower) however it doesn’t work on desktop.