Here is a code of a search filter component where user can find products by typing letters inside the input field. I wonder which improvements can i add. I was thinking of removing some logic into custom hook, but don’t consider it necessary since I use it only once.
p.s. I know that it would be better to use sth like Stack Exchange for such kind of question, but I wanna get the answer.
Component:
export const SearchItems = ({
openBackdrop,
setOpenBackDrop,
}: SearchItemsProps) => {
const theme = useTheme();
const [searchQ, setSearchQ] = useState<string>("");
const [isScrollDisabled, setIsScrollDisabled] = useState<boolean>(false);
const = useState<Product[]>([]);
const [productsByQ, setProductsByQ] = useState<Product[]>([]);
useEffect(() => {
const getProducts = async () => {
try {
const productsRef = await getDocs(collection(db, "products"));
const products = productsRef.docs.map((doc) => ({
...doc.data(),
}));
const flattenData: Product[] = products.flatMap(
(product) => product.data
);
setProducts(flattenData);
} catch (error: any) {
console.log(error.message);
}
};
getProducts();
}, []);
const handleFilterProductsByQ = useMemo(
() =>
debounce((searchQ: string) => {
const filteredData = products.filter((product) =>
product.name.toLowerCase().includes(searchQ.toLowerCase())
);
setProductsByQ(filteredData);
}, 300),
);
useEffect(() => {
handleFilterProductsByQ(searchQ);
}, [searchQ]);
useEffect(() => {
isScrollDisabled
? (document.body.style.overflow = "hidden")
: (document.body.style.overflow = "auto");
return () => {
document.body.style.overflow = "auto";
};
}, [isScrollDisabled]);
const handleInputFocus = () => {
window.scrollTo({ top: 0, behavior: "auto" });
setIsScrollDisabled(true);
setOpenBackDrop(true);
};
const handleOutsideClick = () => {
setOpenBackDrop(false);
setIsScrollDisabled(false);
setSearchQ("");
};
return (
<OutsideClickHandler onOutsideClick={handleOutsideClick}>
<Box position="relative">
<OutlinedInput
type="text"
placeholder="Search Item…"
value={searchQ}
onChange={(e) => setSearchQ(e.target.value)}
onFocus={handleInputFocus}
startAdornment={
<InputAdornment
position="start"
sx={{
color: theme.palette.primary.main,
fontSize: "1.2rem",
}}
>
<FiSearch />
</InputAdornment>
}
endAdornment={
<InputAdornment position="end">
<IconButton
edge="end"
onClick={() => setSearchQ("")}
sx={{
color: theme.palette.secondary.light,
cursor: "pointer",
fontSize: "0.1rem",
}}
>
{searchQ !== "" && <CloseIcon />}
</IconButton>
</InputAdornment>
}
sx={{
borderRadius: "0.8rem",
border: "none",
backgroundColor:
openBackdrop === true ? "#f9fafb" : theme.palette.primary.light,
zIndex: openBackdrop === true ? 9999 : "none",
"& .MuiOutlinedInput-notchedOutline": {
border: "none",
},
}}
/>
{searchQ.length !== 0 && (
<SearchResult searchQ={searchQ} productsByQ={productsByQ} />
)}
</Box>
</OutsideClickHandler>
);
};