Filtering items from an array by category?
Unanswered
tom_bombadil posted this in #help-forum
Hi everyone, I'm currently working on a pet adoption app and I'm stuck on the filtering part. Namely, what I would like to achieve here is that when the user selects one of the categories from the filter and presses the "refresh" button, I filter out all items with that category at that moment. What I have done so far is to create a FilterMenu component that looks like this:
after that I called it on the page where I want to filter the existing animals from the array but for some reason it doesn't filter as I would like, i.e. when I select one of the filter options and press the button for refresh, in the path nothing happens, i.e. only when I select the filter 'age' and confirm in the path I get
but also nothing changes in the list of items below, I still get the whole list, unfiltered. Would anyone have time to look a little deeper into the code and advise me on how I could solve this? I would like to mention that I use mongodb and prisma for the database.
const FilterMenu: React.FC = () => {
const router = useRouter();
const searchParams = useSearchParams();
const [category, setCategory] = useState(searchParams.get("category") || "");
const [city, setCity] = useState(searchParams.get("city") || "");
const [gender, setGender] = useState(searchParams.get("gender") || "");
const [age, setAge] = useState(searchParams.get("age") || "");
const handleFilterChange = (filter: string, value: string) => {
const params = new URLSearchParams(searchParams);
if (value) {
params.set(filter, value);
} else {
params.delete(filter);
}
router.push(/adoptPet?${params.toString()});
};
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
handleFilterChange("category", category);
handleFilterChange("grad", city);
handleFilterChange("spol", gender);
handleFilterChange("starost", age);
};after that I called it on the page where I want to filter the existing animals from the array but for some reason it doesn't filter as I would like, i.e. when I select one of the filter options and press the button for refresh, in the path nothing happens, i.e. only when I select the filter 'age' and confirm in the path I get
'http://localhost: 3000/adoptPet?age=adult'but also nothing changes in the list of items below, I still get the whole list, unfiltered. Would anyone have time to look a little deeper into the code and advise me on how I could solve this? I would like to mention that I use mongodb and prisma for the database.
30 Replies
@"use php"
@tom_bombadil Hi everyone, I'm currently working on a pet adoption app and I'm stuck on the filtering part. Namely, what I would like to achieve here is that when the user selects one of the categories from the filter and presses the "refresh" button, I filter out all items with that category at that moment. What I have done so far is to create a FilterMenu component that looks like this:
`const FilterMenu: React.FC = () => {
const router = useRouter();
const searchParams = useSearchParams();
const [category, setCategory] = useState(searchParams.get("category") || "");
const [city, setCity] = useState(searchParams.get("city") || "");
const [gender, setGender] = useState(searchParams.get("gender") || "");
const [age, setAge] = useState(searchParams.get("age") || "");
const handleFilterChange = (filter: string, value: string) => {
const params = new URLSearchParams(searchParams);
if (value) {
params.set(filter, value);
} else {
params.delete(filter);
}
router.push(`/adoptPet?${params.toString()}`);
};
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
handleFilterChange("category", category);
handleFilterChange("grad", city);
handleFilterChange("spol", gender);
handleFilterChange("starost", age);
};`
after that I called it on the page where I want to filter the existing animals from the array but for some reason it doesn't filter as I would like, i.e. when I select one of the filter options and press the button for refresh, in the path nothing happens, i.e. only when I select the filter 'age' and confirm in the path I get
`'http://localhost: 3000/adoptPet?age=adult' `
but also nothing changes in the list of items below, I still get the whole list, unfiltered. Would anyone have time to look a little deeper into the code and advise me on how I could solve this? I would like to mention that I use mongodb and prisma for the database.
for useState, I recommend using [nuqs](https://www.npmjs.com/package/nuqs)
How are you getting the list?
export default function AdoptPet() {
const router = useRouter();
const searchParams = useSearchParams();
const searchText = (searchParams) || '';
const [posts, setPosts] = useState<Post[]>([]);
const [filteredPosts, setFilteredPosts] = useState<Post[]>(posts);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(12);
const [total, setTotal] = useState(0);
const [isPending, startTransition] = useTransition(); // loading state
const [isLoading, setIsLoading] = useState(true); // Add loading state
const fetchData = (
page: number
) => {
startTransition(async () => {
const result = await getAdoptPost({ query: { page } });
setPosts(result.post);
setPage(result.page);
setPageSize(result.pageSize);
setTotal(result.total);
setIsLoading(false); // Set loading to false after data is fetched
});
};
useEffect(() => {
const currentPage = parseInt(searchParams.get("page") || "1", 12);
fetchData(currentPage);
}, [searchParams]);
const handlePagination = (newPage: number) => {
setIsLoading(true); // Set loading to true when pagination changes
const params = new URLSearchParams(searchParams);
params.set("page", newPage.toString());
router.push(/adoptPet?${params.toString()});
};
const handleNextPage = () => {
if (page < Math.ceil(total / pageSize)) {
handlePagination(page + 1);
}
};
const handlePreviousPage = () => {
if (page > 1) {
handlePagination(page - 1);
}
};here is the page when I rendered all animals
now I will send you jsx
it's about prisma, I will send you now. This is the whole file:
}
"use server";
import { db } from "@public/lib/db";
export async function getLostPetPost(context: any, animals: string) {
const page = parseInt((context.query.page as string) || "1", 12);
const pageSize = 12;
// in function after context:any, add animals:string
// after that, insert in prisma function where: {animalCategory: animals}
// with that, by default if it's empty string it will return all animals,
// but if it contains for example "pas", it will list only "pas posts"
const post = await db.lostPetPost.findMany({
skip: (page - 1) * pageSize,
take: pageSize,
orderBy: {
createdAt: "desc",
},
});
const total = await db.lostPetPost.count({
where: {
animalCategory: animals,
},
});
return {
post,
page,
pageSize,
total,
};
}
export async function getAdoptPost(context: any) {
const page = parseInt((context.query.page as string) || "1", 12);
const pageSize = 12;
const total = await db.adoptAnimal.count({});
const post = await db.adoptAnimal.findMany({
skip: (page - 1) * pageSize,
take: pageSize,
orderBy: {
createdAt: "desc",
},
});
return {
post,
page,
pageSize,
total,
};
}
export async function getDonationPost(context: any) {
const page = parseInt((context.query.page as string) || "1", 12);
const pageSize = 12;
const total = await db.donationPost.count();
const post = await db.donationPost.findMany({
skip: (page - 1) * pageSize,
take: pageSize,
orderBy: {
createdAt: "desc",
},
});
return {
post,
page,
pageSize,
total,
};}
first of all, server actions should only be used for mutations
they cannot run in parallel
oke
which means I can't do filtering that way?
and that is actually the data from the database that is rendered on that page, now I should filter it by some categories
this is how looks like FilterMenu component on the screen
I would like when the filters are selected and the button is clicked, to filter the items from this array
So now can you just tell the exact issue you are facing
so for example if I select the filter 'age (starost)' and the option 'adults (odrasli)', I get in the path '
http://localhost: 3000/adoptPet?age=adults' but it also does not affect the results below, they are all still rendered and they should only 'adults'.this is my FilterMenu component
What have you tried, and whats the issue you are facing
and currently it only puts the last filter that I select in the path, if I add the others it doesn't add them to the path at all, is there a problem with useSearchParams() maybe?
only this selected filter going to the pathname, if I pick up another one and press the button, it will only leave this "starost"
{posts.map((animal) => (
<div key={animal.id} className="p-4 bg-white rounded-xl shadow-md">
<Image
src={animal.imageUrls[0]}
alt={animal.petName}
height={50}
width={50}
unoptimized
className="object-cover rounded-t-2xl h-[35vh] shadow-lg w-full"
/>
<div className="w-full px-5">
<ul className="text-black mt-2 flex flex-col">
<li className="flex items-center">
{animal.category == "pas" ? (
<PiDogBold className="text-[#2F5382] text-lg" />
) : animal.category == "macka" ? (
<FaCat className="text-[#2F5382] text-lg" />
) : (
<SiAnimalplanet className="text-[#2F5382] text-xl" />
)}
<span className="pl-3">
{animal.petName.substring(0, 20)}
{animal.petName.length > 10 ? "..." : ""}
</span>
</li>
<li className="flex items-center">
{animal.spol == "musko" ? (
<IoIosMale className="text-[#2F5382] text-lg" />
) : (
<IoMaleFemale className="text-red-600 text-xl" />
)}
<span className="pl-3">{animal.spol}</span>
</li>
<li className="flex items-center">
<IoLocationOutline className="text-[#2F5382] text-lg" />
<span className="pl-3">{animal.location}</span>
</li>
<li className="flex items-center">
<MdOutlinePets className="text-[#2F5382] text-lg" />
<span className="pl-3">{animal.starost}</span>
</li>
</ul>...
and this is JSX of adopt-page where I rendering all animals
I tried to filter out only animals that are of adult age, and after the refresh button, all 9 items from the list are still rendered, and only those with the "adult" category should be rendered, did I explain better now?
this is an object of one item from the list and its fields '
category', 'starost', 'spol' should be part of my filter and I should filter the results through those selected fieldsconst FilterMenu: React.FC = () => {
const router = useRouter();
const searchParams = useSearchParams();
const [category, setCategory] = useState(searchParams.get("category") || "");
const [location, setLocation] = useState(searchParams.get("location") || "");
const [spol, setSpol] = useState(searchParams.get("spol") || "");
const [starost, setStarost] = useState(searchParams.get("starost") || "");
const handleFilterChange = (filter: string, value: string) => {
const params = new URLSearchParams(searchParams);
if (value) {
params.set(filter, value);
} else {
params.delete(filter);
}
router.push(/adoptPet?${params.toString()});
};
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
handleFilterChange("category", category);
handleFilterChange("grad", location);
handleFilterChange("spol", spol);
handleFilterChange("starost", starost);
// Trigger fetch on server-side via router push with search params
const params = new URLSearchParams({ category, location, spol, starost });
// router.push(/adoptPet/${params.toString()});
};
return (
<div className="border rounded-[24px] p-4 bg-white shadow-lg">
<form
onSubmit={handleSubmit}
className="flex items-center space-x-4 justify-center mb-3"
>
<div className="flex flex-col flex-1">
<label htmlFor="category" className="mb-1 text-black text-[12px]">
Kategorija
</label>
<select
id="category"
name="category"
value={category}
onChange={(e) => setCategory(e.target.value)}
className="p-2 border rounded-[24px] bg-white text-[#A4A4A4] text-[12px]"
>
<option value="Pas">Pas</option>
<option value="Macka">Mačka</option>
<option value="Ostalo">Ostale životinje</option>
</select>
</div> ..is this FilterMenu logic ok?